More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 1,022 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Claim | 15709442 | 780 days ago | IN | 0 ETH | 0.00128439 | ||||
Claim | 13659197 | 1102 days ago | IN | 0 ETH | 0.05224076 | ||||
Claim | 13627094 | 1107 days ago | IN | 0 ETH | 0.05919883 | ||||
Payout | 12910117 | 1218 days ago | IN | 0 ETH | 0.00102421 | ||||
Claim | 12910088 | 1219 days ago | IN | 0 ETH | 0.00121431 | ||||
Claim | 12825726 | 1232 days ago | IN | 0 ETH | 0.01188111 | ||||
Claim | 12628856 | 1262 days ago | IN | 0 ETH | 0.00342562 | ||||
Claim | 11633949 | 1416 days ago | IN | 0 ETH | 0.02729535 | ||||
Claim | 11628637 | 1417 days ago | IN | 0 ETH | 0.04622916 | ||||
Claim | 10882939 | 1531 days ago | IN | 0 ETH | 0.19210005 | ||||
Claim | 10460008 | 1596 days ago | IN | 0 ETH | 0.01008612 | ||||
Claim | 10108917 | 1651 days ago | IN | 0 ETH | 0.01821888 | ||||
Claim | 9642530 | 1723 days ago | IN | 0 ETH | 0.00240145 | ||||
Claim | 9495543 | 1746 days ago | IN | 0 ETH | 0.00739666 | ||||
Claim | 9468065 | 1750 days ago | IN | 0 ETH | 0.00218974 | ||||
Claim | 9466614 | 1750 days ago | IN | 0 ETH | 0.00240145 | ||||
Claim | 9456406 | 1752 days ago | IN | 0 ETH | 0.00049627 | ||||
Claim | 9435268 | 1755 days ago | IN | 0 ETH | 0.0002235 | ||||
Claim | 9435259 | 1755 days ago | IN | 0 ETH | 0.00240145 | ||||
Claim | 9423506 | 1757 days ago | IN | 0 ETH | 0.00184916 | ||||
Claim | 9397043 | 1761 days ago | IN | 0 ETH | 0.00046229 | ||||
Claim | 9391936 | 1762 days ago | IN | 0 ETH | 0.00092458 | ||||
Claim | 9386079 | 1762 days ago | IN | 0 ETH | 0.0000447 | ||||
Claim | 9386074 | 1762 days ago | IN | 0 ETH | 0.0002001 | ||||
Claim | 9383579 | 1763 days ago | IN | 0 ETH | 0.00109487 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
ETOCommitment
Compiler Version
v0.4.26+commit.4563c3fc
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2019-10-03 */ pragma solidity 0.4.26; /// @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 //////////////////////// constructor(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 emit LogAccessPolicyChanged(msg.sender, oldPolicy, newPolicy); } function accessPolicy() public constant returns (IAccessPolicy) { return _accessPolicy; } } /// @title standard access roles of the Platform /// @dev constants are kept in CODE not in STORAGE so they are comparatively cheap 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 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; // may setup whitelists and abort whitelisting contract with curve rollback bytes32 internal constant ROLE_WHITELIST_ADMIN = 0xaef456e7c864418e1d2a40d996ca4febf3a7e317fe3af5a7ea4dda59033bbe5c; // allows to deposit EUR-T and allow addresses to send and receive EUR-T. keccak256("EurtDepositManager") bytes32 internal constant ROLE_EURT_DEPOSIT_MANAGER = 0x7c8ecdcba80ce87848d16ad77ef57cc196c208fc95c5638e4a48c681a34d4fe7; // allows to register identities and change associated claims keccak256("IdentityManager") bytes32 internal constant ROLE_IDENTITY_MANAGER = 0x32964e6bc50f2aaab2094a1d311be8bda920fc4fb32b2fb054917bdb153a9e9e; // allows to replace controller on euro token and to destroy tokens without withdraw kecckak256("EurtLegalManager") bytes32 internal constant ROLE_EURT_LEGAL_MANAGER = 0x4eb6b5806954a48eb5659c9e3982d5e75bfb2913f55199877d877f157bcc5a9b; // allows to change known interfaces in universe kecckak256("UniverseManager") bytes32 internal constant ROLE_UNIVERSE_MANAGER = 0xe8d8f8f9ea4b19a5a4368dbdace17ad71a69aadeb6250e54c7b4c7b446301738; // allows to exchange gas for EUR-T keccak("GasExchange") bytes32 internal constant ROLE_GAS_EXCHANGE = 0x9fe43636e0675246c99e96d7abf9f858f518b9442c35166d87f0934abef8a969; // allows to set token exchange rates keccak("TokenRateOracle") bytes32 internal constant ROLE_TOKEN_RATE_ORACLE = 0xa80c3a0c8a5324136e4c806a778583a2a980f378bdd382921b8d28dcfe965585; // allows to disburse to the fee disbursal contract keccak("Disburser") bytes32 internal constant ROLE_DISBURSER = 0xd7ea6093d11d866c9e8449f8bffd9da1387c530ee40ad54f0641425bb0ca33b7; // allows to manage feedisbursal controller keccak("DisbursalManager") bytes32 internal constant ROLE_DISBURSAL_MANAGER = 0x677f87f7b7ef7c97e42a7e6c85c295cf020c9f11eea1e49f6bf847d7aeae1475; // allows to upgrade company/issuer contracts which are also equity token controllers keccak("CompanyUpgradeAdmin") bytes32 internal constant ROLE_COMPANY_UPGRADE_ADMIN = 0xfef15747c403732d986b29a92a880d8f2fb886b99417c8bbef226f85885ca924; } 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 smart conctract legal representation 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 smart contract legal 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 IAgreement { //////////////////////// // Events //////////////////////// event LogAgreementAccepted( address indexed accepter ); event LogAgreementAmended( address contractLegalRepresentative, string agreementUri ); /// @dev should have access restrictions so only contractLegalRepresentative may call function amendAgreement(string agreementUri) public; /// returns information on last amendment of the agreement /// @dev MUST revert if no agreements were set function currentAgreement() public constant returns ( address contractLegalRepresentative, uint256 signedBlockTimestamp, string agreementUri, uint256 index ); /// returns information on amendment with index /// @dev MAY revert on non existing amendment, indexing starts from 0 function pastAgreement(uint256 amendmentIndex) public constant returns ( address contractLegalRepresentative, uint256 signedBlockTimestamp, string agreementUri, uint256 index ); /// returns the number of block at wchich `signatory` signed agreement /// @dev MUST return 0 if not signed function agreementSignedAtBlock(address signatory) public constant returns (uint256 blockNo); /// returns number of amendments made by contractLegalRepresentative function amendmentsCount() public constant returns (uint256); } /** * @title legally binding smart contract * @dev read IAgreement for details **/ contract Agreement is IAgreement, AccessControlled, AccessRoles { //////////////////////// // Type declarations //////////////////////// /// @notice agreement with signature of the platform operator representative struct SignedAgreement { address contractLegalRepresentative; 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; //////////////////////// // 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) { acceptAgreementInternal(accepter); _; } modifier onlyLegalRepresentative(address legalRepresentative) { require(mCanAmend(legalRepresentative)); _; } //////////////////////// // Constructor //////////////////////// constructor(IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter) AccessControlled(accessPolicy) internal { require(forkArbiter != IEthereumForkArbiter(0x0)); ETHEREUM_FORK_ARBITER = forkArbiter; } //////////////////////// // Public functions //////////////////////// function amendAgreement(string agreementUri) public onlyLegalRepresentative(msg.sender) { SignedAgreement memory amendment = SignedAgreement({ contractLegalRepresentative: msg.sender, signedBlockTimestamp: block.timestamp, agreementUri: agreementUri }); _amendments.push(amendment); emit LogAgreementAmended(msg.sender, agreementUri); } function ethereumForkArbiter() public constant returns (IEthereumForkArbiter) { return ETHEREUM_FORK_ARBITER; } function currentAgreement() public constant returns ( address contractLegalRepresentative, uint256 signedBlockTimestamp, string agreementUri, uint256 index ) { require(_amendments.length > 0); uint256 last = _amendments.length - 1; SignedAgreement storage amendment = _amendments[last]; return ( amendment.contractLegalRepresentative, amendment.signedBlockTimestamp, amendment.agreementUri, last ); } function pastAgreement(uint256 amendmentIndex) public constant returns ( address contractLegalRepresentative, uint256 signedBlockTimestamp, string agreementUri, uint256 index ) { SignedAgreement storage amendment = _amendments[amendmentIndex]; return ( amendment.contractLegalRepresentative, amendment.signedBlockTimestamp, amendment.agreementUri, amendmentIndex ); } function agreementSignedAtBlock(address signatory) public constant returns (uint256 blockNo) { return _signatories[signatory]; } function amendmentsCount() public constant returns (uint256) { return _amendments.length; } //////////////////////// // Internal functions //////////////////////// /// provides direct access to derived contract function acceptAgreementInternal(address accepter) internal { if(_signatories[accepter] == 0) { require(_amendments.length > 0); _signatories[accepter] = block.number; emit LogAgreementAccepted(accepter); } } // // MAgreement Internal interface (todo: extract) // /// default amend permission goes to ROLE_PLATFORM_OPERATOR_REPRESENTATIVE function mCanAmend(address legalRepresentative) internal returns (bool) { return accessPolicy().allowed(legalRepresentative, ROLE_PLATFORM_OPERATOR_REPRESENTATIVE, this, msg.sig); } } /// @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); } 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); } 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 ITokenMetadata { //////////////////////// // Public functions //////////////////////// function symbol() public constant returns (string); function name() public constant returns (string); function decimals() public constant returns (uint8); } contract IERC223Token is IERC20Token, ITokenMetadata { /// @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 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 { } /// @title hooks token controller to token contract and allows to replace it contract ITokenControllerHook { //////////////////////// // Events //////////////////////// event LogChangeTokenController( address oldController, address newController, address by ); //////////////////////// // Public functions //////////////////////// /// @notice replace current token controller /// @dev please note that this process is also controlled by existing controller function changeTokenController(address newController) public; /// @notice returns current controller function tokenController() public constant returns (address currentController); } /// @title state space of ETOCommitment contract IETOCommitmentStates { //////////////////////// // Types //////////////////////// // order must reflect time precedence, do not change order below enum ETOState { Setup, // Initial state Whitelist, Public, Signing, Claim, Payout, // Terminal state Refund // Terminal state } // number of states in enum uint256 constant internal ETO_STATES_COUNT = 7; } /// @title provides callback on state transitions /// @dev observer called after the state() of commitment contract was set contract IETOCommitmentObserver is IETOCommitmentStates { function commitmentObserver() public constant returns (address); function onStateTransition(ETOState oldState, ETOState newState) public; } /// @title current ERC223 fallback function /// @dev to be used in all future token contract /// @dev NEU and ICBMEtherToken (obsolete) are the only contracts that still uses IERC223LegacyCallback contract IERC223Callback { //////////////////////// // Public functions //////////////////////// function tokenFallback(address from, uint256 amount, bytes data) public; } /// @title granular token controller based on MSnapshotToken observer pattern contract ITokenController { //////////////////////// // Public functions //////////////////////// /// @notice see MTokenTransferController /// @dev additionally passes broker that is executing transaction between from and to /// for unbrokered transfer, broker == from function onTransfer(address broker, address from, address to, uint256 amount) public constant returns (bool allow); /// @notice see MTokenAllowanceController function onApprove(address owner, address spender, uint256 amount) public constant returns (bool allow); /// @notice see MTokenMint function onGenerateTokens(address sender, address owner, uint256 amount) public constant returns (bool allow); /// @notice see MTokenMint function onDestroyTokens(address sender, address owner, uint256 amount) public constant returns (bool allow); /// @notice controls if sender can change controller to newController /// @dev for this to succeed TYPICALLY current controller must be already migrated to a new one function onChangeTokenController(address sender, address newController) public constant returns (bool); /// @notice overrides spender allowance /// @dev may be used to implemented forced transfers in which token controller may override approved allowance /// with any > 0 value and then use transferFrom to execute such transfer /// This by definition creates non-trustless token so do not implement this call if you do not need trustless transfers! /// Implementer should not allow approve() to be executed if there is an overrride // Implemented should return allowance() taking into account override function onAllowance(address owner, address spender) public constant returns (uint256 allowanceOverride); } contract IEquityTokenController is IAgreement, ITokenController, IETOCommitmentObserver, IERC223Callback { /// controls if sender can change old nominee to new nominee /// @dev for this to succeed typically a voting of the token holders should happen and new nominee should be set function onChangeNominee(address sender, address oldNominee, address newNominee) public constant returns (bool); } contract IEquityToken is IAgreement, IClonedTokenParent, IERC223Token, ITokenControllerHook { /// @dev equity token is not divisible (Decimals == 0) but single share is represented by /// tokensPerShare tokens function tokensPerShare() public constant returns (uint256); // number of shares represented by tokens. we round to the closest value. function sharesTotalSupply() public constant returns (uint256); /// nominal value of a share in decimal(18) precision in currency as per token controller ISHA function shareNominalValueUlps() public constant returns (uint256); // returns company legal representative account that never changes function companyLegalRepresentative() public constant returns (address); /// returns current nominee which is contract legal rep function nominee() public constant returns (address); /// only by previous nominee function changeNominee(address newNominee) public; /// controlled, always issues to msg.sender function issueTokens(uint256 amount) public; /// controlled, may send tokens even when transfer are disabled: to active ETO only function distributeTokens(address to, uint256 amount) public; // controlled, msg.sender is typically failed ETO function destroyTokens(uint256 amount) public; } /// @title describes layout of claims in 256bit records stored for identities /// @dev intended to be derived by contracts requiring access to particular claims contract IdentityRecord { //////////////////////// // Types //////////////////////// /// @dev here the idea is to have claims of size of uint256 and use this struct /// to translate in and out of this struct. until we do not cross uint256 we /// have binary compatibility struct IdentityClaims { bool isVerified; // 1 bit bool isSophisticatedInvestor; // 1 bit bool hasBankAccount; // 1 bit bool accountFrozen; // 1 bit bool requiresRegDAccreditation; // 1 bit bool hasValidRegDAccreditation; // 1 bit // uint250 reserved } //////////////////////// // Internal functions //////////////////////// /// translates uint256 to struct function deserializeClaims(bytes32 data) internal pure returns (IdentityClaims memory claims) { // for memory layout of struct, each field below word length occupies whole word // todo: shift to SHR instruction assembly { mstore(claims, and(data, 0x1)) mstore(add(claims, 0x20), div(and(data, 0x2), 0x2)) mstore(add(claims, 0x40), div(and(data, 0x4), 0x4)) mstore(add(claims, 0x60), div(and(data, 0x8), 0x8)) mstore(add(claims, 0x80), div(and(data, 0x10), 0x10)) mstore(add(claims, 0xA0), div(and(data, 0x20), 0x20)) } } } /// @title interface storing and retrieve 256bit claims records for identity /// actual format of record is decoupled from storage (except maximum size) contract IIdentityRegistry { //////////////////////// // Events //////////////////////// /// provides information on setting claims event LogSetClaims( address indexed identity, bytes32 oldClaims, bytes32 newClaims ); //////////////////////// // Public functions //////////////////////// /// get claims for identity function getClaims(address identity) public constant returns (bytes32); /// set claims for identity /// @dev odlClaims and newClaims used for optimistic locking. to override with newClaims /// current claims must be oldClaims function setClaims(address identity, bytes32 oldClaims, bytes32 newClaims) public; } /// @title known interfaces (services) of the platform /// "known interface" is a unique id of service provided by the platform and discovered via Universe contract /// it does not refer to particular contract/interface ABI, particular service may be delivered via different implementations /// however for a few contracts we commit platform to particular implementation (all ICBM Contracts, Universe itself etc.) /// @dev constants are kept in CODE not in STORAGE so they are comparatively cheap contract KnownInterfaces { //////////////////////// // Constants //////////////////////// // NOTE: All interface are set to the keccak256 hash of the // CamelCased interface or singleton name, i.e. // KNOWN_INTERFACE_NEUMARK = keccak256("Neumark") // EIP 165 + EIP 820 should be use instead but it seems they are far from finished // also interface signature should be build automatically by solidity. otherwise it is a pure hassle // neumark token interface and sigleton keccak256("Neumark") bytes4 internal constant KNOWN_INTERFACE_NEUMARK = 0xeb41a1bd; // ether token interface and singleton keccak256("EtherToken") bytes4 internal constant KNOWN_INTERFACE_ETHER_TOKEN = 0x8cf73cf1; // euro token interface and singleton keccak256("EuroToken") bytes4 internal constant KNOWN_INTERFACE_EURO_TOKEN = 0x83c3790b; // euro token interface and singleton keccak256("EuroTokenController") bytes4 internal constant KNOWN_INTERFACE_EURO_TOKEN_CONTROLLER = 0x33ac4661; // identity registry interface and singleton keccak256("IIdentityRegistry") bytes4 internal constant KNOWN_INTERFACE_IDENTITY_REGISTRY = 0x0a72e073; // currency rates oracle interface and singleton keccak256("ITokenExchangeRateOracle") bytes4 internal constant KNOWN_INTERFACE_TOKEN_EXCHANGE_RATE_ORACLE = 0xc6e5349e; // fee disbursal interface and singleton keccak256("IFeeDisbursal") bytes4 internal constant KNOWN_INTERFACE_FEE_DISBURSAL = 0xf4c848e8; // platform portfolio holding equity tokens belonging to NEU holders keccak256("IPlatformPortfolio"); bytes4 internal constant KNOWN_INTERFACE_PLATFORM_PORTFOLIO = 0xaa1590d0; // token exchange interface and singleton keccak256("ITokenExchange") bytes4 internal constant KNOWN_INTERFACE_TOKEN_EXCHANGE = 0xddd7a521; // service exchanging euro token for gas ("IGasTokenExchange") bytes4 internal constant KNOWN_INTERFACE_GAS_EXCHANGE = 0x89dbc6de; // access policy interface and singleton keccak256("IAccessPolicy") bytes4 internal constant KNOWN_INTERFACE_ACCESS_POLICY = 0xb05049d9; // euro lock account (upgraded) keccak256("LockedAccount:Euro") bytes4 internal constant KNOWN_INTERFACE_EURO_LOCK = 0x2347a19e; // ether lock account (upgraded) keccak256("LockedAccount:Ether") bytes4 internal constant KNOWN_INTERFACE_ETHER_LOCK = 0x978a6823; // icbm euro lock account keccak256("ICBMLockedAccount:Euro") bytes4 internal constant KNOWN_INTERFACE_ICBM_EURO_LOCK = 0x36021e14; // ether lock account (upgraded) keccak256("ICBMLockedAccount:Ether") bytes4 internal constant KNOWN_INTERFACE_ICBM_ETHER_LOCK = 0x0b58f006; // ether token interface and singleton keccak256("ICBMEtherToken") bytes4 internal constant KNOWN_INTERFACE_ICBM_ETHER_TOKEN = 0xae8b50b9; // euro token interface and singleton keccak256("ICBMEuroToken") bytes4 internal constant KNOWN_INTERFACE_ICBM_EURO_TOKEN = 0xc2c6cd72; // ICBM commitment interface interface and singleton keccak256("ICBMCommitment") bytes4 internal constant KNOWN_INTERFACE_ICBM_COMMITMENT = 0x7f2795ef; // ethereum fork arbiter interface and singleton keccak256("IEthereumForkArbiter") bytes4 internal constant KNOWN_INTERFACE_FORK_ARBITER = 0x2fe7778c; // Platform terms interface and singletong keccak256("PlatformTerms") bytes4 internal constant KNOWN_INTERFACE_PLATFORM_TERMS = 0x75ecd7f8; // for completness we define Universe service keccak256("Universe"); bytes4 internal constant KNOWN_INTERFACE_UNIVERSE = 0xbf202454; // ETO commitment interface (collection) keccak256("ICommitment") bytes4 internal constant KNOWN_INTERFACE_COMMITMENT = 0xfa0e0c60; // Equity Token Controller interface (collection) keccak256("IEquityTokenController") bytes4 internal constant KNOWN_INTERFACE_EQUITY_TOKEN_CONTROLLER = 0xfa30b2f1; // Equity Token interface (collection) keccak256("IEquityToken") bytes4 internal constant KNOWN_INTERFACE_EQUITY_TOKEN = 0xab9885bb; // Payment tokens (collection) keccak256("PaymentToken") bytes4 internal constant KNOWN_INTERFACE_PAYMENT_TOKEN = 0xb2a0042a; // ETO Contraints, aka Products keccak256("ETOTermsConstraints") bytes4 internal constant KNOWN_INTERFACE_ETO_TERMS_CONSTRAINTS = 0xce2be4f5; } /// @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 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 //////////////////////// constructor( 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 `onlyMigrationEnabled` modifier in implementation function migrate() 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; emit LogMigrationEnabled(_migration); } /// @notice returns current migration target function currentMigrationTarget() public constant returns (IMigrationTarget) { return _migration; } } contract IsContract { //////////////////////// // Internal functions //////////////////////// function isContract(address addr) internal constant returns (bool) { uint256 size; // takes 700 gas assembly { size := extcodesize(addr) } return size > 0; } } /// @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 ICBMLockedAccount 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(address(this).balance); } else { uint256 balance = token.balanceOf(this); require(token.transfer(reclaimer, balance)); } } } /// @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 constructor( 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; } } /// @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); /// @notice Allows to override allowance approved by the owner /// Primary role is to enable forced transfers, do not override if you do not like it /// Following behavior is expected in the observer /// approve() - should revert if mAllowanceOverride() > 0 /// allowance() - should return mAllowanceOverride() if set /// transferFrom() - should override allowance if mAllowanceOverride() > 0 /// @param owner An address giving allowance to spender /// @param spender An address getting a right to transfer allowance amount from the owner /// @return current allowance amount function mAllowanceOverride( address owner, address spender ) internal constant returns (uint256 allowance); } /// @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 { } contract TrustlessTokenController is MTokenController { //////////////////////// // Internal functions //////////////////////// // // Implements MTokenController // function mOnTransfer( address /*from*/, address /*to*/, uint256 /*amount*/ ) internal returns (bool allow) { return true; } function mOnApprove( address /*owner*/, address /*spender*/, uint256 /*amount*/ ) internal returns (bool allow) { return true; } } 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 Math { //////////////////////// // Internal functions //////////////////////// // absolute difference: |v1 - v2| function absDiff(uint256 v1, uint256 v2) internal pure 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 pure 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 pure 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 pure returns(uint256) { return divRound(mul(amount, part), total); } // // Open Zeppelin Math library below // function mul(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a * b; assert(a == 0 || c / a == b); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } } /// @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 Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is MTokenTransfer, MTokenTransferController, 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) { mTransfer(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 //////////////////////// // // Implements MTokenTransfer // function mTransfer(address from, address to, uint256 amount) internal { require(to != address(0)); require(mOnTransfer(from, to, amount)); _balances[from] = sub(_balances[from], amount); _balances[to] = add(_balances[to], amount); emit Transfer(from, to, amount); } } /// @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 // owner => spender => amount mapping (address => mapping (address => uint256)) private _allowed; //////////////////////// // Constructor //////////////////////// constructor() 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) { uint256 override = mAllowanceOverride(owner, spender); if (override > 0) { return override; } 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) && mAllowanceOverride(msg.sender, spender) == 0); _allowed[msg.sender][spender] = amount; emit 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) { uint256 allowed = mAllowanceOverride(from, msg.sender); if (allowed == 0) { // The standard ERC 20 transferFrom functionality allowed = _allowed[from][msg.sender]; // yes this will underflow but then we'll revert. will cost gas however so don't underflow _allowed[from][msg.sender] -= amount; } require(allowed >= 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; } //////////////////////// // Internal functions //////////////////////// // // Implements default MTokenAllowanceController // // no override in default implementation function mAllowanceOverride( address /*owner*/, address /*spender*/ ) internal constant returns (uint256) { return 0; } } /** * @title Standard ERC20 token * * @dev Implementation of the standard token. * @dev https://github.com/ethereum/EIPs/issues/20 */ contract StandardToken is IERC20Token, BasicToken, TokenAllowance { } /// @title uniquely identifies deployable (non-abstract) platform contract /// @notice cheap way of assigning implementations to knownInterfaces which represent system services /// unfortunatelly ERC165 does not include full public interface (ABI) and does not provide way to list implemented interfaces /// EIP820 still in the making /// @dev ids are generated as follows keccak256("neufund-platform:<contract name>") /// ids roughly correspond to ABIs contract IContractId { /// @param id defined as above /// @param version implementation version function contractId() public pure returns (bytes32 id, uint256 version); } contract IWithdrawableToken { //////////////////////// // Public functions //////////////////////// /// @notice withdraws from a token holding assets /// @dev amount of asset should be returned to msg.sender and corresponding balance burned function withdraw(uint256 amount) public; } contract EtherToken is IsContract, IContractId, AccessControlled, StandardToken, TrustlessTokenController, IWithdrawableToken, TokenMetadata, IERC223Token, 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 ); event LogWithdrawAndSend( address indexed from, address indexed to, uint256 amount ); //////////////////////// // Constructor //////////////////////// constructor(IAccessPolicy accessPolicy) AccessControlled(accessPolicy) StandardToken() TokenMetadata(NAME, DECIMALS, SYMBOL, "") Reclaimable() public { } //////////////////////// // Public functions //////////////////////// /// deposit msg.value of Ether to msg.sender balance function deposit() public payable { depositPrivate(); emit Transfer(address(0), msg.sender, msg.value); } /// @notice convenience function to deposit and immediately transfer amount /// @param transferTo where to transfer after deposit /// @param amount total amount to transfer, must be <= balance after deposit /// @param data erc223 data /// @dev intended to deposit from simple account and invest in ETO function depositAndTransfer(address transferTo, uint256 amount, bytes data) public payable { depositPrivate(); transfer(transferTo, amount, data); } /// withdraws and sends 'amount' of ether to msg.sender function withdraw(uint256 amount) public { withdrawPrivate(amount); msg.sender.transfer(amount); } /// @notice convenience function to withdraw and transfer to external account /// @param sendTo address to which send total amount /// @param amount total amount to withdraw and send /// @dev function is payable and is meant to withdraw funds on accounts balance and token in single transaction /// @dev BEWARE that msg.sender of the funds is Ether Token contract and not simple account calling it. /// @dev when sent to smart conctract funds may be lost, so this is prevented below function withdrawAndSend(address sendTo, uint256 amount) public payable { // must send at least what is in msg.value to being another deposit function require(amount >= msg.value, "NF_ET_NO_DEPOSIT"); if (amount > msg.value) { uint256 withdrawRemainder = amount - msg.value; withdrawPrivate(withdrawRemainder); } emit LogWithdrawAndSend(msg.sender, sendTo, amount); sendTo.transfer(amount); } // // Implements IERC223Token // function transfer(address to, uint256 amount, bytes data) public returns (bool) { BasicToken.mTransfer(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).tokenFallback(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); } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x75b86bc24f77738576716a36431588ae768d80d077231d1661c2bea674c6373a, 0); } //////////////////////// // Private functions //////////////////////// function depositPrivate() private { _balances[msg.sender] = add(_balances[msg.sender], msg.value); _totalSupply = add(_totalSupply, msg.value); emit LogDeposit(msg.sender, msg.value); } function withdrawPrivate(uint256 amount) private { require(_balances[msg.sender] >= amount); _balances[msg.sender] = sub(_balances[msg.sender], amount); _totalSupply = sub(_totalSupply, amount); emit LogWithdrawal(msg.sender, amount); emit Transfer(msg.sender, address(0), amount); } } contract EuroToken is Agreement, IERC677Token, StandardToken, IWithdrawableToken, ITokenControllerHook, TokenMetadata, IERC223Token, IsContract, IContractId { //////////////////////// // Constants //////////////////////// string private constant NAME = "Euro Token"; string private constant SYMBOL = "EUR-T"; uint8 private constant DECIMALS = 18; //////////////////////// // Mutable state //////////////////////// ITokenController private _tokenController; //////////////////////// // Events //////////////////////// /// on each deposit (increase of supply) of EUR-T /// 'by' indicates account that executed the deposit function for 'to' (typically bank connector) event LogDeposit( address indexed to, address by, uint256 amount, bytes32 reference ); // proof of requested deposit initiated by token holder event LogWithdrawal( address indexed from, uint256 amount ); // proof of settled deposit event LogWithdrawSettled( address from, address by, // who settled uint256 amount, // settled amount, after fees, negative interest rates etc. uint256 originalAmount, // original amount withdrawn bytes32 withdrawTxHash, // hash of withdraw transaction bytes32 reference // reference number of withdraw operation at deposit manager ); /// on destroying the tokens without withdraw (see `destroyTokens` function below) event LogDestroy( address indexed from, address by, uint256 amount ); //////////////////////// // Modifiers //////////////////////// modifier onlyIfDepositAllowed(address to, uint256 amount) { require(_tokenController.onGenerateTokens(msg.sender, to, amount)); _; } modifier onlyIfWithdrawAllowed(address from, uint256 amount) { require(_tokenController.onDestroyTokens(msg.sender, from, amount)); _; } //////////////////////// // Constructor //////////////////////// constructor( IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter, ITokenController tokenController ) Agreement(accessPolicy, forkArbiter) StandardToken() TokenMetadata(NAME, DECIMALS, SYMBOL, "") public { require(tokenController != ITokenController(0x0)); _tokenController = tokenController; } //////////////////////// // Public functions //////////////////////// /// @notice deposit 'amount' of EUR-T to address 'to', attaching correlating `reference` to LogDeposit event /// @dev deposit may happen only in case 'to' can receive transfer in token controller /// by default KYC is required to receive deposits function deposit(address to, uint256 amount, bytes32 reference) public only(ROLE_EURT_DEPOSIT_MANAGER) onlyIfDepositAllowed(to, amount) acceptAgreement(to) { require(to != address(0)); _balances[to] = add(_balances[to], amount); _totalSupply = add(_totalSupply, amount); emit LogDeposit(to, msg.sender, amount, reference); emit Transfer(address(0), to, amount); } /// @notice runs many deposits within one transaction /// @dev deposit may happen only in case 'to' can receive transfer in token controller /// by default KYC is required to receive deposits function depositMany(address[] to, uint256[] amount, bytes32[] reference) public { require(to.length == amount.length); require(to.length == reference.length); for (uint256 i = 0; i < to.length; i++) { deposit(to[i], amount[i], reference[i]); } } /// @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. based on that proof deposit manager will make a bank transfer /// by default controller will check the following: KYC and existence of working bank account function withdraw(uint256 amount) public onlyIfWithdrawAllowed(msg.sender, amount) acceptAgreement(msg.sender) { destroyTokensPrivate(msg.sender, amount); emit LogWithdrawal(msg.sender, amount); } /// @notice issued by deposit manager when withdraw request was settled. typicaly amount that could be settled will be lower /// than amount withdrawn: banks charge negative interest rates and various fees that must be deduced /// reference number is attached that may be used to identify withdraw operation at deposit manager function settleWithdraw(address from, uint256 amount, uint256 originalAmount, bytes32 withdrawTxHash, bytes32 reference) public only(ROLE_EURT_DEPOSIT_MANAGER) { emit LogWithdrawSettled(from, msg.sender, amount, originalAmount, withdrawTxHash, reference); } /// @notice this method allows to destroy EUR-T belonging to any account /// note that EURO is fiat currency and is not trustless, EUR-T is also /// just internal currency of Neufund platform, not general purpose stable coin /// we need to be able to destroy EUR-T if ordered by authorities function destroy(address owner, uint256 amount) public only(ROLE_EURT_LEGAL_MANAGER) { destroyTokensPrivate(owner, amount); emit LogDestroy(owner, msg.sender, amount); } // // Implements ITokenControllerHook // function changeTokenController(address newController) public { require(_tokenController.onChangeTokenController(msg.sender, newController)); _tokenController = ITokenController(newController); emit LogChangeTokenController(_tokenController, newController, msg.sender); } function tokenController() public constant returns (address) { return _tokenController; } // // Implements IERC223Token // function transfer(address to, uint256 amount, bytes data) public returns (bool success) { return ierc223TransferInternal(msg.sender, to, amount, data); } /// @notice convenience function to deposit and immediately transfer amount /// @param depositTo which account to deposit to and then transfer from /// @param transferTo where to transfer after deposit /// @param depositAmount amount to deposit /// @param transferAmount total amount to transfer, must be <= balance after deposit /// @dev intended to deposit from bank account and invest in ETO function depositAndTransfer( address depositTo, address transferTo, uint256 depositAmount, uint256 transferAmount, bytes data, bytes32 reference ) public returns (bool success) { deposit(depositTo, depositAmount, reference); return ierc223TransferInternal(depositTo, transferTo, transferAmount, data); } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0xfb5c7e43558c4f3f5a2d87885881c9b10ff4be37e3308579c178bf4eaa2c29cd, 0); } //////////////////////// // Internal functions //////////////////////// // // Implements MTokenController // function mOnTransfer( address from, address to, uint256 amount ) internal acceptAgreement(from) returns (bool allow) { address broker = msg.sender; if (broker != from) { // if called by the depositor (deposit and send), ignore the broker flag bool isDepositor = accessPolicy().allowed(msg.sender, ROLE_EURT_DEPOSIT_MANAGER, this, msg.sig); // this is not very clean but alternative (give brokerage rights to all depositors) is maintenance hell if (isDepositor) { broker = from; } } return _tokenController.onTransfer(broker, from, to, amount); } function mOnApprove( address owner, address spender, uint256 amount ) internal acceptAgreement(owner) returns (bool allow) { return _tokenController.onApprove(owner, spender, amount); } function mAllowanceOverride( address owner, address spender ) internal constant returns (uint256) { return _tokenController.onAllowance(owner, spender); } // // Observes MAgreement internal interface // /// @notice euro token is legally represented by separate entity ROLE_EURT_LEGAL_MANAGER function mCanAmend(address legalRepresentative) internal returns (bool) { return accessPolicy().allowed(legalRepresentative, ROLE_EURT_LEGAL_MANAGER, this, msg.sig); } //////////////////////// // Private functions //////////////////////// function destroyTokensPrivate(address owner, uint256 amount) private { require(_balances[owner] >= amount); _balances[owner] = sub(_balances[owner], amount); _totalSupply = sub(_totalSupply, amount); emit Transfer(owner, address(0), amount); } /// @notice internal transfer function that checks permissions and calls the tokenFallback function ierc223TransferInternal(address from, address to, uint256 amount, bytes data) private returns (bool success) { BasicToken.mTransfer(from, 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).tokenFallback(from, amount, data); } return true; } } /// @title serialization of basic types from/to bytes contract Serialization { //////////////////////// // Internal functions //////////////////////// function decodeAddress(bytes b) internal pure returns (address a) { require(b.length == 20); assembly { // load memory area that is address "carved out" of 64 byte bytes. prefix is zeroed a := and(mload(add(b, 20)), 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) } } function decodeAddressUInt256(bytes b) internal pure returns (address a, uint256 i) { require(b.length == 52); assembly { // load memory area that is address "carved out" of 64 byte bytes. prefix is zeroed a := and(mload(add(b, 20)), 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) i := mload(add(b, 52)) } } } 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 pure 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 pure 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 pure 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 pure 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 pure 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 pure returns (uint256) { return NEUMARK_CAP; } function initialRewardFraction() public pure 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 mAdvanceSnapshotId() internal returns (uint256); // this is a version of mAdvanceSnapshotId that does not modify state but MUST return the same value // it is required to implement ITokenSnapshots interface cleanly function mCurrentSnapshotId() internal constant returns (uint256); } /// @title creates new snapshot id on each day boundary /// @dev snapshot id is unix timestamp of current day boundary contract Daily is MSnapshotPolicy { //////////////////////// // Constants //////////////////////// // Floor[2**128 / 1 days] uint256 private MAX_TIMESTAMP = 3938453320844195178974243141571391; //////////////////////// // Constructor //////////////////////// /// @param start snapshotId from which to start generating values, used to prevent cloning from incompatible schemes /// @dev start must be for the same day or 0, required for token cloning constructor(uint256 start) internal { // 0 is invalid value as we are past unix epoch if (start > 0) { uint256 base = dayBase(uint128(block.timestamp)); // must be within current day base require(start >= base); // dayBase + 2**128 will not overflow as it is based on block.timestamp require(start < base + 2**128); } } //////////////////////// // Public functions //////////////////////// function snapshotAt(uint256 timestamp) public constant returns (uint256) { require(timestamp < MAX_TIMESTAMP); return dayBase(uint128(timestamp)); } //////////////////////// // Internal functions //////////////////////// // // Implements MSnapshotPolicy // function mAdvanceSnapshotId() internal returns (uint256) { return mCurrentSnapshotId(); } function mCurrentSnapshotId() internal constant returns (uint256) { // disregard overflows on block.timestamp, see MAX_TIMESTAMP return dayBase(uint128(block.timestamp)); } function dayBase(uint128 timestamp) internal pure returns (uint256) { // Round down to the start of the day (00:00 UTC) and place in higher 128bits return 2**128 * (uint256(timestamp) / 1 days); } } /// @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 Daily, ISnapshotable { //////////////////////// // 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 constructor(uint256 start) internal Daily(start) { if (start > 0) { _currentSnapshotId = start; } } //////////////////////// // Public functions //////////////////////// // // Implements ISnapshotable // function createSnapshot() public returns (uint256) { uint256 base = dayBase(uint128(block.timestamp)); if (base > _currentSnapshotId) { // New day has started, create snapshot for midnight _currentSnapshotId = base; } else { // within single day, increase counter (assume 2**128 will not be crossed) _currentSnapshotId += 1; } // Log and return emit LogSnapshotCreated(_currentSnapshotId); return _currentSnapshotId; } //////////////////////// // Internal functions //////////////////////// // // Implements MSnapshotPolicy // function mAdvanceSnapshotId() internal returns (uint256) { uint256 base = dayBase(uint128(block.timestamp)); // New day has started if (base > _currentSnapshotId) { _currentSnapshotId = base; emit LogSnapshotCreated(base); } return _currentSnapshotId; } function mCurrentSnapshotId() internal constant returns (uint256) { uint256 base = dayBase(uint128(block.timestamp)); return base > _currentSnapshotId ? base : _currentSnapshotId; } } /// @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 = mAdvanceSnapshotId(); // 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 token with snapshots and transfer functionality /// @dev observes MTokenTransferController interface /// observes ISnapshotToken interface /// implementes MTokenTransfer interface contract BasicSnapshotToken is MTokenTransfer, MTokenTransferController, IClonedTokenParent, IBasicToken, 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. constructor( 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) internal 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 parent 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 uint256 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 emit 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 constructor( 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); emit 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); emit 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 MintableSnapshotToken, TokenAllowance { //////////////////////// // 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 constructor( IClonedTokenParent parentToken, uint256 parentSnapshotId ) MintableSnapshotToken(parentToken, parentSnapshotId) TokenAllowance() internal {} } /// @title old ERC223 callback function /// @dev as used in Neumark and ICBMEtherToken contract IERC223LegacyCallback { //////////////////////// // Public functions //////////////////////// function onTokenTransfer(address from, uint256 amount, bytes data) public; } contract Neumark is AccessControlled, AccessRoles, Agreement, DailyAndSnapshotable, StandardSnapshotToken, TokenMetadata, IERC223Token, NeumarkIssuanceCurve, Reclaimable, IsContract { //////////////////////// // 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 //////////////////////// constructor( IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter ) 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); emit 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); } // // Implements IERC223Token with IERC223Callback (onTokenTransfer) callback // // old implementation of ERC223 that was actual when ICBM was deployed // as Neumark is already deployed this function keeps old behavior for testing 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)) { IERC223LegacyCallback(to).onTokenTransfer(msg.sender, amount, data); } return true; } //////////////////////// // 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; emit LogNeumarksBurned(msg.sender, euroUlps, burnNeumarkUlps); } } /// @title makes modern ERC223 contracts compatible with the legacy implementation /// @dev should be used for all receivers of tokens sent by ICBMEtherToken and NEU contract ERC223LegacyCallbackCompat { //////////////////////// // Public functions //////////////////////// function onTokenTransfer(address wallet, uint256 amount, bytes data) public { tokenFallback(wallet, amount, data); } function tokenFallback(address wallet, uint256 amount, bytes data) public; } /// @title granular fee disbursal controller contract IFeeDisbursalController is IContractId { //////////////////////// // Public functions //////////////////////// /// @notice check whether claimer can accept disbursal offer function onAccept(address /*token*/, address /*proRataToken*/, address claimer) public constant returns (bool allow); /// @notice check whether claimer can reject disbursal offer function onReject(address /*token*/, address /*proRataToken*/, address claimer) public constant returns (bool allow); /// @notice check wether this disbursal can happen function onDisburse(address token, address disburser, uint256 amount, address proRataToken, uint256 recycleAfterPeriod) public constant returns (bool allow); /// @notice check wether this recycling can happen function onRecycle(address token, address /*proRataToken*/, address[] investors, uint256 until) public constant returns (bool allow); /// @notice check wether the disbursal controller may be changed function onChangeFeeDisbursalController(address sender, IFeeDisbursalController newController) public constant returns (bool); } /// @title disburse payment token amount to snapshot token holders /// @dev payment token received via ERC223 Transfer contract IFeeDisbursal is IERC223Callback, IERC677Callback, IERC223LegacyCallback, ERC223LegacyCallbackCompat, IContractId { //////////////////////// // Events //////////////////////// event LogDisbursalCreated( address indexed proRataToken, address indexed token, uint256 amount, uint256 recycleAfterDuration, address disburser, uint256 index ); event LogDisbursalAccepted( address indexed claimer, address token, address proRataToken, uint256 amount, uint256 nextIndex ); event LogDisbursalRejected( address indexed claimer, address token, address proRataToken, uint256 amount, uint256 nextIndex ); event LogFundsRecycled( address indexed proRataToken, address indexed token, uint256 amount, address by ); event LogChangeFeeDisbursalController( address oldController, address newController, address by ); //////////////////////// // Types //////////////////////// struct Disbursal { // snapshop ID of the pro-rata token, which will define which amounts to disburse against uint256 snapshotId; // amount of tokens to disburse uint256 amount; // timestamp after which claims to this token can be recycled uint128 recycleableAfterTimestamp; // timestamp on which token were disbursed uint128 disbursalTimestamp; // contract sending the disbursal address disburser; } //////////////////////// // Constants //////////////////////// uint256 internal constant UINT256_MAX = 2**256 - 1; //////////////////////// // Public functions //////////////////////// /// @notice get the disbursal at a given index for a given token /// @param token address of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param index until what index to claim to function getDisbursal(address token, address proRataToken, uint256 index) public constant returns ( uint256 snapshotId, uint256 amount, uint256 recycleableAfterTimestamp, uint256 disburseTimestamp, address disburser ); /// @notice get disbursals for current snapshot id of the proRataToken that cannot be claimed yet /// @param token address of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @return array of (snapshotId, amount, index) ordered by index. full disbursal information can be retrieved via index function getNonClaimableDisbursals(address token, address proRataToken) public constant returns (uint256[3][] memory disbursals); /// @notice get count of disbursals for given token /// @param token address of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken function getDisbursalCount(address token, address proRataToken) public constant returns (uint256); /// @notice accepts the token disbursal offer and claim offered tokens, to be called by an investor /// @param token address of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param until until what index to claim to, noninclusive, use 2**256 to accept all disbursals function accept(address token, ITokenSnapshots proRataToken, uint256 until) public; /// @notice accepts disbursals of multiple tokens and receives them, to be called an investor /// @param tokens addresses of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken function acceptMultipleByToken(address[] tokens, ITokenSnapshots proRataToken) public; /// @notice accepts disbursals for single token against many pro rata tokens /// @param token address of the disbursable token /// @param proRataTokens addresses of the tokens used to determine the user pro rata amount, must be a snapshottoken /// @dev this should let save a lot on gas by eliminating multiple transfers and some checks function acceptMultipleByProRataToken(address token, ITokenSnapshots[] proRataTokens) public; /// @notice rejects disbursal of token which leads to recycle and disbursal of rejected amount /// @param token address of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param until until what index to claim to, noninclusive, use 2**256 to reject all disbursals function reject(address token, ITokenSnapshots proRataToken, uint256 until) public; /// @notice check how many tokens of a certain kind can be claimed by an account /// @param token address of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param claimer address of the claimer that would receive the funds /// @param until until what index to claim to, noninclusive, use 2**256 to reject all disbursals /// @return (amount that can be claimed, total disbursed amount, time to recycle of first disbursal, first disbursal index) function claimable(address token, ITokenSnapshots proRataToken, address claimer, uint256 until) public constant returns (uint256 claimableAmount, uint256 totalAmount, uint256 recycleableAfterTimestamp, uint256 firstIndex); /// @notice check how much fund for each disbursable tokens can be claimed by claimer /// @param tokens addresses of the disbursable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param claimer address of the claimer that would receive the funds /// @return array of (amount that can be claimed, total disbursed amount, time to recycle of first disbursal, first disbursal index) /// @dev claimbles are returned in the same order as tokens were specified function claimableMutipleByToken(address[] tokens, ITokenSnapshots proRataToken, address claimer) public constant returns (uint256[4][] claimables); /// @notice check how many tokens can be claimed against many pro rata tokens /// @param token address of the disbursable token /// @param proRataTokens addresses of the tokens used to determine the user pro rata amount, must be a snapshottoken /// @param claimer address of the claimer that would receive the funds /// @return array of (amount that can be claimed, total disbursed amount, time to recycle of first disbursal, first disbursal index) function claimableMutipleByProRataToken(address token, ITokenSnapshots[] proRataTokens, address claimer) public constant returns (uint256[4][] claimables); /// @notice recycle a token for multiple investors /// @param token address of the recyclable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param investors list of investors we want to recycle tokens for /// @param until until what index to recycle to function recycle(address token, ITokenSnapshots proRataToken, address[] investors, uint256 until) public; /// @notice check how much we can recycle for multiple investors /// @param token address of the recyclable token /// @param proRataToken address of the token used to determine the user pro rata amount, must be a snapshottoken /// @param investors list of investors we want to recycle tokens for /// @param until until what index to recycle to function recycleable(address token, ITokenSnapshots proRataToken, address[] investors, uint256 until) public constant returns (uint256); /// @notice get current controller function feeDisbursalController() public constant returns (IFeeDisbursalController); /// @notice update current controller function changeFeeDisbursalController(IFeeDisbursalController newController) public; } /// @title disburse payment token amount to snapshot token holders /// @dev payment token received via ERC223 Transfer contract IPlatformPortfolio is IERC223Callback { // TODO: declare interface } contract ITokenExchangeRateOracle { /// @notice provides actual price of 'numeratorToken' in 'denominatorToken' /// returns timestamp at which price was obtained in oracle function getExchangeRate(address numeratorToken, address denominatorToken) public constant returns (uint256 rateFraction, uint256 timestamp); /// @notice allows to retreive multiple exchange rates in once call function getExchangeRates(address[] numeratorTokens, address[] denominatorTokens) public constant returns (uint256[] rateFractions, uint256[] timestamps); } /// @title root of trust and singletons + known interface registry /// provides a root which holds all interfaces platform trust, this includes /// singletons - for which accessors are provided /// collections of known instances of interfaces /// @dev interfaces are identified by bytes4, see KnownInterfaces.sol contract Universe is Agreement, IContractId, KnownInterfaces { //////////////////////// // Events //////////////////////// /// raised on any change of singleton instance /// @dev for convenience we provide previous instance of singleton in replacedInstance event LogSetSingleton( bytes4 interfaceId, address instance, address replacedInstance ); /// raised on add/remove interface instance in collection event LogSetCollectionInterface( bytes4 interfaceId, address instance, bool isSet ); //////////////////////// // Mutable state //////////////////////// // mapping of known contracts to addresses of singletons mapping(bytes4 => address) private _singletons; // mapping of known interfaces to collections of contracts mapping(bytes4 => mapping(address => bool)) private _collections; // solium-disable-line indentation // known instances mapping(address => bytes4[]) private _instances; //////////////////////// // Constructor //////////////////////// constructor( IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter ) Agreement(accessPolicy, forkArbiter) public { setSingletonPrivate(KNOWN_INTERFACE_ACCESS_POLICY, accessPolicy); setSingletonPrivate(KNOWN_INTERFACE_FORK_ARBITER, forkArbiter); } //////////////////////// // Public methods //////////////////////// /// get singleton instance for 'interfaceId' function getSingleton(bytes4 interfaceId) public constant returns (address) { return _singletons[interfaceId]; } function getManySingletons(bytes4[] interfaceIds) public constant returns (address[]) { address[] memory addresses = new address[](interfaceIds.length); uint256 idx; while(idx < interfaceIds.length) { addresses[idx] = _singletons[interfaceIds[idx]]; idx += 1; } return addresses; } /// checks of 'instance' is instance of interface 'interfaceId' function isSingleton(bytes4 interfaceId, address instance) public constant returns (bool) { return _singletons[interfaceId] == instance; } /// checks if 'instance' is one of instances of 'interfaceId' function isInterfaceCollectionInstance(bytes4 interfaceId, address instance) public constant returns (bool) { return _collections[interfaceId][instance]; } function isAnyOfInterfaceCollectionInstance(bytes4[] interfaceIds, address instance) public constant returns (bool) { uint256 idx; while(idx < interfaceIds.length) { if (_collections[interfaceIds[idx]][instance]) { return true; } idx += 1; } return false; } /// gets all interfaces of given instance function getInterfacesOfInstance(address instance) public constant returns (bytes4[] interfaces) { return _instances[instance]; } /// sets 'instance' of singleton with interface 'interfaceId' function setSingleton(bytes4 interfaceId, address instance) public only(ROLE_UNIVERSE_MANAGER) { setSingletonPrivate(interfaceId, instance); } /// convenience method for setting many singleton instances function setManySingletons(bytes4[] interfaceIds, address[] instances) public only(ROLE_UNIVERSE_MANAGER) { require(interfaceIds.length == instances.length); uint256 idx; while(idx < interfaceIds.length) { setSingletonPrivate(interfaceIds[idx], instances[idx]); idx += 1; } } /// set or unset 'instance' with 'interfaceId' in collection of instances function setCollectionInterface(bytes4 interfaceId, address instance, bool set) public only(ROLE_UNIVERSE_MANAGER) { setCollectionPrivate(interfaceId, instance, set); } /// set or unset 'instance' in many collections of instances function setInterfaceInManyCollections(bytes4[] interfaceIds, address instance, bool set) public only(ROLE_UNIVERSE_MANAGER) { uint256 idx; while(idx < interfaceIds.length) { setCollectionPrivate(interfaceIds[idx], instance, set); idx += 1; } } /// set or unset array of collection function setCollectionsInterfaces(bytes4[] interfaceIds, address[] instances, bool[] set_flags) public only(ROLE_UNIVERSE_MANAGER) { require(interfaceIds.length == instances.length); require(interfaceIds.length == set_flags.length); uint256 idx; while(idx < interfaceIds.length) { setCollectionPrivate(interfaceIds[idx], instances[idx], set_flags[idx]); idx += 1; } } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x8b57bfe21a3ef4854e19d702063b6cea03fa514162f8ff43fde551f06372fefd, 0); } //////////////////////// // Getters //////////////////////// function accessPolicy() public constant returns (IAccessPolicy) { return IAccessPolicy(_singletons[KNOWN_INTERFACE_ACCESS_POLICY]); } function forkArbiter() public constant returns (IEthereumForkArbiter) { return IEthereumForkArbiter(_singletons[KNOWN_INTERFACE_FORK_ARBITER]); } function neumark() public constant returns (Neumark) { return Neumark(_singletons[KNOWN_INTERFACE_NEUMARK]); } function etherToken() public constant returns (IERC223Token) { return IERC223Token(_singletons[KNOWN_INTERFACE_ETHER_TOKEN]); } function euroToken() public constant returns (IERC223Token) { return IERC223Token(_singletons[KNOWN_INTERFACE_EURO_TOKEN]); } function etherLock() public constant returns (address) { return _singletons[KNOWN_INTERFACE_ETHER_LOCK]; } function euroLock() public constant returns (address) { return _singletons[KNOWN_INTERFACE_EURO_LOCK]; } function icbmEtherLock() public constant returns (address) { return _singletons[KNOWN_INTERFACE_ICBM_ETHER_LOCK]; } function icbmEuroLock() public constant returns (address) { return _singletons[KNOWN_INTERFACE_ICBM_EURO_LOCK]; } function identityRegistry() public constant returns (address) { return IIdentityRegistry(_singletons[KNOWN_INTERFACE_IDENTITY_REGISTRY]); } function tokenExchangeRateOracle() public constant returns (address) { return ITokenExchangeRateOracle(_singletons[KNOWN_INTERFACE_TOKEN_EXCHANGE_RATE_ORACLE]); } function feeDisbursal() public constant returns (address) { return IFeeDisbursal(_singletons[KNOWN_INTERFACE_FEE_DISBURSAL]); } function platformPortfolio() public constant returns (address) { return IPlatformPortfolio(_singletons[KNOWN_INTERFACE_PLATFORM_PORTFOLIO]); } function tokenExchange() public constant returns (address) { return _singletons[KNOWN_INTERFACE_TOKEN_EXCHANGE]; } function gasExchange() public constant returns (address) { return _singletons[KNOWN_INTERFACE_GAS_EXCHANGE]; } function platformTerms() public constant returns (address) { return _singletons[KNOWN_INTERFACE_PLATFORM_TERMS]; } //////////////////////// // Private methods //////////////////////// function setSingletonPrivate(bytes4 interfaceId, address instance) private { require(interfaceId != KNOWN_INTERFACE_UNIVERSE, "NF_UNI_NO_UNIVERSE_SINGLETON"); address replacedInstance = _singletons[interfaceId]; // do nothing if not changing if (replacedInstance != instance) { dropInstance(replacedInstance, interfaceId); addInstance(instance, interfaceId); _singletons[interfaceId] = instance; } emit LogSetSingleton(interfaceId, instance, replacedInstance); } function setCollectionPrivate(bytes4 interfaceId, address instance, bool set) private { // do nothing if not changing if (_collections[interfaceId][instance] == set) { return; } _collections[interfaceId][instance] = set; if (set) { addInstance(instance, interfaceId); } else { dropInstance(instance, interfaceId); } emit LogSetCollectionInterface(interfaceId, instance, set); } function addInstance(address instance, bytes4 interfaceId) private { if (instance == address(0)) { // do not add null instance return; } bytes4[] storage current = _instances[instance]; uint256 idx; while(idx < current.length) { // instancy has this interface already, do nothing if (current[idx] == interfaceId) return; idx += 1; } // new interface current.push(interfaceId); } function dropInstance(address instance, bytes4 interfaceId) private { if (instance == address(0)) { // do not drop null instance return; } bytes4[] storage current = _instances[instance]; uint256 idx; uint256 last = current.length - 1; while(idx <= last) { if (current[idx] == interfaceId) { // delete element if (idx < last) { // if not last element move last element to idx being deleted current[idx] = current[last]; } // delete last element current.length -= 1; return; } idx += 1; } } } /// @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(), "NF_INV_SOURCE"); _; } } /// @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 ICBMLockedAccountMigration is MigrationTarget { //////////////////////// // Public functions //////////////////////// // implemented in migration target, apply `onlyMigrationSource()` modifier, modifiers are not inherited function migrateInvestor( address investor, uint256 balance, uint256 neumarksDue, uint256 unlockDate ) public; } /// @title standard access roles of the Platform /// @dev constants are kept in CODE not in STORAGE so they are comparatively cheap contract ICBMRoles { //////////////////////// // 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; } contract TimeSource { //////////////////////// // Internal functions //////////////////////// function currentTime() internal constant returns (uint256) { return block.timestamp; } } contract ICBMLockedAccount is AccessControlled, ICBMRoles, 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 constructor( IAccessPolicy policy, IERC677Token assetToken, Neumark neumark, address penaltyDisbursalAddress, uint256 lockPeriod, uint256 penaltyFraction ) 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; } emit 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)); ICBMLockedAccountMigration(_migration).migrateInvestor( msg.sender, account.balance, account.neumarksDue, account.unlockDate ); emit 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); emit 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)); } emit 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)); emit LogFundsUnlocked(investor, accountInMem.balance, accountInMem.neumarksDue); } } contract LockedAccount is Agreement, Math, Serialization, ICBMLockedAccountMigration, IdentityRecord, KnownInterfaces, Reclaimable, IContractId { //////////////////////// // Type declarations //////////////////////// /// represents locked account of the investor struct Account { // funds locked in the account uint112 balance; // neumark amount that must be returned to unlock uint112 neumarksDue; // date with which unlock may happen without penalty uint32 unlockDate; } /// represents account migration destination /// @notice migration destinations require KYC when being set /// @dev used to setup migration to different wallet if for some reason investors /// wants to use different wallet in the Platform than ICBM. /// @dev it also allows to split the tickets, neumarks due will be split proportionally struct Destination { // destination wallet address investor; // amount to be migrated to wallet above. 0 means all funds uint112 amount; } //////////////////////// // Immutable state //////////////////////// // token that stores investors' funds IERC223Token private PAYMENT_TOKEN; Neumark private NEUMARK; // longstop period in seconds uint256 private LOCK_PERIOD; // penalty: decimalFraction of stored amount on escape hatch uint256 private PENALTY_FRACTION; // interface registry Universe private UNIVERSE; // icbm locked account which is migration source ICBMLockedAccount private MIGRATION_SOURCE; // old payment token IERC677Token private OLD_PAYMENT_TOKEN; //////////////////////// // Mutable state //////////////////////// // total amount of tokens locked uint112 private _totalLockedAmount; // total number of locked investors uint256 internal _totalInvestors; // all accounts mapping(address => Account) internal _accounts; // tracks investment to be able to control refunds (commitment => investor => account) mapping(address => mapping(address => Account)) internal _commitments; // account migration destinations mapping(address => Destination[]) private _destinations; //////////////////////// // Events //////////////////////// /// @notice logged when funds are committed to token offering /// @param investor address /// @param commitment commitment contract where funds were sent /// @param amount amount of invested funds /// @param amount of corresponging Neumarks that successful offering will "unlock" event LogFundsCommitted( address indexed investor, address indexed commitment, 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 investor account is migrated /// @param investor address receiving the migration /// @param amount amount of newly migrated 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 funds/obligations moved to different address /// @param oldInvestor current address /// @param newInvestor destination address /// @dev see move function for comments /*event LogInvestorMoved( address indexed oldInvestor, address indexed newInvestor );*/ /// @notice logged when funds are locked as a refund by commitment contract /// @param investor address of refunded investor /// @param commitment commitment contract sending the refund /// @param amount refund amount /// @param amount of neumarks corresponding to the refund event LogFundsRefunded( address indexed investor, address indexed commitment, 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 paymentToken address of token contract penalty was paid with /// @param investor addres of investor paying penalty /// @dev paymentToken and investor parameters are added for quick tallying penalty payouts event LogPenaltyDisbursed( address indexed disbursalPoolAddress, address indexed investor, uint256 amount, address paymentToken ); /// @notice logged when migration destination is set for an investor event LogMigrationDestination( address indexed investor, address indexed destination, uint256 amount ); //////////////////////// // Modifiers //////////////////////// modifier onlyIfCommitment(address commitment) { // is allowed token offering require(UNIVERSE.isInterfaceCollectionInstance(KNOWN_INTERFACE_COMMITMENT, commitment), "NF_LOCKED_ONLY_COMMITMENT"); _; } //////////////////////// // Constructor //////////////////////// /// @notice creates new LockedAccount instance /// @param universe provides interface and identity registries /// @param paymentToken token contract representing funds locked /// @param migrationSource old locked account constructor( Universe universe, Neumark neumark, IERC223Token paymentToken, ICBMLockedAccount migrationSource ) Agreement(universe.accessPolicy(), universe.forkArbiter()) Reclaimable() public { PAYMENT_TOKEN = paymentToken; MIGRATION_SOURCE = migrationSource; OLD_PAYMENT_TOKEN = MIGRATION_SOURCE.assetToken(); UNIVERSE = universe; NEUMARK = neumark; LOCK_PERIOD = migrationSource.lockPeriod(); PENALTY_FRACTION = migrationSource.penaltyFraction(); // this is not super sexy but it's very practical against attaching ETH wallet to EUR wallet // we decrease chances of migration lethal setup errors in non migrated wallets require(keccak256(abi.encodePacked(ITokenMetadata(OLD_PAYMENT_TOKEN).symbol())) == keccak256(abi.encodePacked(PAYMENT_TOKEN.symbol()))); } //////////////////////// // Public functions //////////////////////// /// @notice commits funds in one of offerings on the platform /// @param commitment commitment contract with token offering /// @param amount amount of funds to invest /// @dev data ignored, to keep compatibility with ERC223 /// @dev happens via ERC223 transfer and callback function transfer(address commitment, uint256 amount, bytes /*data*/) public onlyIfCommitment(commitment) { require(amount > 0, "NF_LOCKED_NO_ZERO"); Account storage account = _accounts[msg.sender]; // no overflow with account.balance which is uint112 require(account.balance >= amount, "NF_LOCKED_NO_FUNDS"); // calculate unlocked NEU as proportion of invested amount to account balance uint112 unlockedNmkUlps = uint112( proportion( account.neumarksDue, amount, account.balance ) ); account.balance = subBalance(account.balance, uint112(amount)); // will not overflow as amount < account.balance so unlockedNmkUlps must be >= account.neumarksDue account.neumarksDue -= unlockedNmkUlps; // track investment Account storage investment = _commitments[address(commitment)][msg.sender]; investment.balance += uint112(amount); investment.neumarksDue += unlockedNmkUlps; // invest via ERC223 interface assert(PAYMENT_TOKEN.transfer(commitment, amount, abi.encodePacked(msg.sender))); emit LogFundsCommitted(msg.sender, commitment, amount, unlockedNmkUlps); } /// @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 { 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, address _token, bytes _data) public returns (bool) { require(msg.sender == _token); require(_data.length == 0); // only from neumarks require(_token == address(NEUMARK), "NF_ONLY_NEU"); // this will check if allowance was made and if _amount is enough to // unlock, reverts on any error condition unlockInvestor(from); return true; } /// @notice refunds investor in case of failed offering /// @param investor funds owner /// @dev callable only by ETO contract, bookkeeping in LockedAccount::_commitments /// @dev expected that ETO makes allowance for transferFrom function refunded(address investor) public { Account memory investment = _commitments[msg.sender][investor]; // return silently when there is no refund (so commitment contracts can blank-call, less gas used) if (investment.balance == 0) return; // free gas here delete _commitments[msg.sender][investor]; Account storage account = _accounts[investor]; // account must exist require(account.unlockDate > 0, "NF_LOCKED_ACCOUNT_LIQUIDATED"); // add refunded amount account.balance = addBalance(account.balance, investment.balance); account.neumarksDue = add112(account.neumarksDue, investment.neumarksDue); // transfer to itself from Commitment contract allowance assert(PAYMENT_TOKEN.transferFrom(msg.sender, address(this), investment.balance)); emit LogFundsRefunded(investor, msg.sender, investment.balance, investment.neumarksDue); } /// @notice may be used by commitment contract to refund gas for commitment bookkeeping /// @dev https://gastoken.io/ (15000 - 900 for a call) function claimed(address investor) public { delete _commitments[msg.sender][investor]; } /// checks commitments made from locked account that were not settled by ETO via refunded or claimed functions function pendingCommitments(address commitment, address investor) public constant returns (uint256 balance, uint256 neumarkDue) { Account storage i = _commitments[commitment][investor]; return (i.balance, i.neumarksDue); } // // Implements LockedAccountMigrationTarget // function migrateInvestor( address investor, uint256 balance256, uint256 neumarksDue256, uint256 unlockDate256 ) public onlyMigrationSource() { // internally we use 112 bits to store amounts require(balance256 < 2**112, "NF_OVR"); uint112 balance = uint112(balance256); assert(neumarksDue256 < 2**112); uint112 neumarksDue = uint112(neumarksDue256); assert(unlockDate256 < 2**32); uint32 unlockDate = uint32(unlockDate256); // transfer assets require(OLD_PAYMENT_TOKEN.transferFrom(msg.sender, address(this), balance)); IWithdrawableToken(OLD_PAYMENT_TOKEN).withdraw(balance); // migrate previous asset token depends on token type, unfortunatelly deposit function differs so we have to cast. this is weak... if (PAYMENT_TOKEN == UNIVERSE.etherToken()) { // after EtherToken withdraw, deposit ether into new token EtherToken(PAYMENT_TOKEN).deposit.value(balance)(); } else { EuroToken(PAYMENT_TOKEN).deposit(this, balance, 0x0); } Destination[] storage destinations = _destinations[investor]; if (destinations.length == 0) { // if no destinations defined then migrate to original investor wallet lock(investor, balance, neumarksDue, unlockDate); } else { // enumerate all destinations and migrate balance piece by piece uint256 idx; while(idx < destinations.length) { Destination storage destination = destinations[idx]; // get partial amount to migrate, if 0 specified then take all, as a result 0 must be the last destination uint112 partialAmount = destination.amount == 0 ? balance : destination.amount; require(partialAmount <= balance, "NF_LOCKED_ACCOUNT_SPLIT_OVERSPENT"); // compute corresponding NEU proportionally, result < 10**18 as partialAmount <= balance uint112 partialNmkUlps = uint112( proportion( neumarksDue, partialAmount, balance ) ); // no overflow see above balance -= partialAmount; // no overflow partialNmkUlps <= neumarksDue as as partialAmount <= balance, see proportion neumarksDue -= partialNmkUlps; // lock partial to destination investor lock(destination.investor, partialAmount, partialNmkUlps, unlockDate); idx += 1; } // all funds and NEU must be migrated require(balance == 0, "NF_LOCKED_ACCOUNT_SPLIT_UNDERSPENT"); assert(neumarksDue == 0); // free up gas delete _destinations[investor]; } } /// @notice changes migration destination for msg.sender /// @param destinationWallet where migrate funds to, must have valid verification claims /// @dev msg.sender has funds in old icbm wallet and calls this function on new icbm wallet before s/he migrates function setInvestorMigrationWallet(address destinationWallet) public { Destination[] storage destinations = _destinations[msg.sender]; // delete old destinations if(destinations.length > 0) { delete _destinations[msg.sender]; } // new destination for the whole amount addDestination(destinations, destinationWallet, 0); } /// @dev if one of amounts is > 2**112, solidity will pass modulo value, so for 2**112 + 1, we'll get 1 /// and that's fine function setInvestorMigrationWallets(address[] wallets, uint112[] amounts) public { require(wallets.length == amounts.length); Destination[] storage destinations = _destinations[msg.sender]; // delete old destinations if(destinations.length > 0) { delete _destinations[msg.sender]; } uint256 idx; while(idx < wallets.length) { addDestination(destinations, wallets[idx], amounts[idx]); idx += 1; } } /// @notice returns current set of destination wallets for investor migration function getInvestorMigrationWallets(address investor) public constant returns (address[] wallets, uint112[] amounts) { Destination[] storage destinations = _destinations[investor]; wallets = new address[](destinations.length); amounts = new uint112[](destinations.length); uint256 idx; while(idx < destinations.length) { wallets[idx] = destinations[idx].investor; amounts[idx] = destinations[idx].amount; idx += 1; } } // // Implements IMigrationTarget // function currentMigrationSource() public constant returns (address) { return address(MIGRATION_SOURCE); } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x15fbe12e85e3698f22c35480f7c66bc38590bb8cfe18cbd6dc3d49355670e561, 0); } // // Payable default function to receive ether during migration // function () public payable { require(msg.sender == address(OLD_PAYMENT_TOKEN)); } // // Overrides Reclaimable // /// @notice allows LockedAccount to reclaim tokens wrongly sent to its address /// @dev as LockedAccount by design has balance of paymentToken (in the name of investors) /// such reclamation is not allowed function reclaim(IBasicToken token) public { // forbid reclaiming locked tokens require(token != PAYMENT_TOKEN, "NO_PAYMENT_TOKEN_RECLAIM"); Reclaimable.reclaim(token); } // // Public accessors // function paymentToken() public constant returns (IERC223Token) { return PAYMENT_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 balance, uint256 neumarksDue, uint32 unlockDate) { Account storage account = _accounts[investor]; return (account.balance, account.neumarksDue, account.unlockDate); } function totalLockedAmount() public constant returns (uint256) { return _totalLockedAmount; } function totalInvestors() public constant returns (uint256) { return _totalInvestors; } //////////////////////// // Internal functions //////////////////////// function addBalance(uint112 balance, uint112 amount) internal returns (uint112) { _totalLockedAmount = add112(_totalLockedAmount, amount); // will not overflow as _totalLockedAmount >= balance return balance + amount; } //////////////////////// // Private functions //////////////////////// function subBalance(uint112 balance, uint112 amount) private returns (uint112) { _totalLockedAmount = sub112(_totalLockedAmount, amount); return sub112(balance, amount); } function removeInvestor(address investor, uint112 balance) private { subBalance(balance, balance); _totalInvestors -= 1; delete _accounts[investor]; } /// @notice unlocks 'investor' tokens by making them withdrawable from paymentToken /// @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 paymentToken, /// before unlockDate, penalty is deduced and distributed 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); // 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 (block.timestamp < accountInMem.unlockDate) { address penaltyDisbursalAddress = UNIVERSE.feeDisbursal(); require(penaltyDisbursalAddress != address(0)); uint112 penalty = uint112(decimalFraction(accountInMem.balance, PENALTY_FRACTION)); // distribution via ERC223 to contract or simple address assert(PAYMENT_TOKEN.transfer(penaltyDisbursalAddress, penalty, abi.encodePacked(NEUMARK))); emit LogPenaltyDisbursed(penaltyDisbursalAddress, investor, penalty, PAYMENT_TOKEN); accountInMem.balance -= penalty; } // transfer amount back to investor - now it can withdraw assert(PAYMENT_TOKEN.transfer(investor, accountInMem.balance, "")); emit LogFundsUnlocked(investor, accountInMem.balance, accountInMem.neumarksDue); } /// @notice locks funds of investors for a period of time, called by migration /// @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 /// @param unlockDate unlockDate of migrating account /// @dev used only by migration function lock(address investor, uint112 amount, uint112 neumarks, uint32 unlockDate) private acceptAgreement(investor) { require(amount > 0); Account storage account = _accounts[investor]; if (account.unlockDate == 0) { // this is new account - unlockDate always > 0 _totalInvestors += 1; } // update holdings account.balance = addBalance(account.balance, amount); account.neumarksDue = add112(account.neumarksDue, neumarks); // overwrite unlockDate if it is earler. we do not supporting joining tickets from different investors // this will discourage sending 1 wei to move unlock date if (unlockDate > account.unlockDate) { account.unlockDate = unlockDate; } emit LogFundsLocked(investor, amount, neumarks); } function addDestination(Destination[] storage destinations, address wallet, uint112 amount) private { // only verified destinations IIdentityRegistry identityRegistry = IIdentityRegistry(UNIVERSE.identityRegistry()); IdentityClaims memory claims = deserializeClaims(identityRegistry.getClaims(wallet)); require(claims.isVerified && !claims.accountFrozen, "NF_DEST_NO_VERIFICATION"); if (wallet != msg.sender) { // prevent squatting - cannot set destination for not yet migrated investor (,,uint256 unlockDate) = MIGRATION_SOURCE.balanceOf(wallet); require(unlockDate == 0, "NF_DEST_NO_SQUATTING"); } destinations.push( Destination({investor: wallet, amount: amount}) ); emit LogMigrationDestination(msg.sender, wallet, amount); } function sub112(uint112 a, uint112 b) internal pure returns (uint112) { assert(b <= a); return a - b; } function add112(uint112 a, uint112 b) internal pure returns (uint112) { uint112 c = a + b; assert(c >= a); return c; } } contract ShareholderRights is IContractId { //////////////////////// // Types //////////////////////// enum VotingRule { // nominee has no voting rights NoVotingRights, // nominee votes yes if token holders do not say otherwise Positive, // nominee votes against if token holders do not say otherwise Negative, // nominee passes the vote as is giving yes/no split Proportional } //////////////////////// // Constants state //////////////////////// bytes32 private constant EMPTY_STRING_HASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; //////////////////////// // Immutable state //////////////////////// // todo: split into ShareholderRights and TokenholderRigths where the first one corresponds to rights of real shareholder (nominee, founder) // and the second one corresponds to the list of the token holder (which does not own shares but have identical rights (equity token)) // or has a debt token with very different rights // TokenholderRights will be attached to a token via TokenController and will for example say if token participates in dividends or shareholder resolutins // a right to drag along (or be dragged) on exit bool public constant HAS_DRAG_ALONG_RIGHTS = true; // a right to tag along bool public constant HAS_TAG_ALONG_RIGHTS = true; // information is fundamental right that cannot be removed bool public constant HAS_GENERAL_INFORMATION_RIGHTS = true; // voting Right VotingRule public GENERAL_VOTING_RULE; // voting rights in tag along VotingRule public TAG_ALONG_VOTING_RULE; // liquidation preference multiplicator as decimal fraction uint256 public LIQUIDATION_PREFERENCE_MULTIPLIER_FRAC; // founder's vesting bool public HAS_FOUNDERS_VESTING; // duration of general voting uint256 public GENERAL_VOTING_DURATION; // duration of restricted act votings (like exit etc.) uint256 public RESTRICTED_ACT_VOTING_DURATION; // offchain time to finalize and execute voting; uint256 public VOTING_FINALIZATION_DURATION; // quorum of shareholders for the vote to count as decimal fraction uint256 public SHAREHOLDERS_VOTING_QUORUM_FRAC; // number of tokens voting / total supply must be more than this to count the vote uint256 public VOTING_MAJORITY_FRAC = 10**17; // 10% // url (typically IPFS hash) to investment agreement between nominee and company string public INVESTMENT_AGREEMENT_TEMPLATE_URL; //////////////////////// // Constructor //////////////////////// constructor( VotingRule generalVotingRule, VotingRule tagAlongVotingRule, uint256 liquidationPreferenceMultiplierFrac, bool hasFoundersVesting, uint256 generalVotingDuration, uint256 restrictedActVotingDuration, uint256 votingFinalizationDuration, uint256 shareholdersVotingQuorumFrac, uint256 votingMajorityFrac, string investmentAgreementTemplateUrl ) public { // todo: revise requires require(uint(generalVotingRule) < 4); require(uint(tagAlongVotingRule) < 4); // quorum < 100% require(shareholdersVotingQuorumFrac <= 10**18); require(keccak256(abi.encodePacked(investmentAgreementTemplateUrl)) != EMPTY_STRING_HASH); GENERAL_VOTING_RULE = generalVotingRule; TAG_ALONG_VOTING_RULE = tagAlongVotingRule; LIQUIDATION_PREFERENCE_MULTIPLIER_FRAC = liquidationPreferenceMultiplierFrac; HAS_FOUNDERS_VESTING = hasFoundersVesting; GENERAL_VOTING_DURATION = generalVotingDuration; RESTRICTED_ACT_VOTING_DURATION = restrictedActVotingDuration; VOTING_FINALIZATION_DURATION = votingFinalizationDuration; SHAREHOLDERS_VOTING_QUORUM_FRAC = shareholdersVotingQuorumFrac; VOTING_MAJORITY_FRAC = votingMajorityFrac; INVESTMENT_AGREEMENT_TEMPLATE_URL = investmentAgreementTemplateUrl; } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x7f46caed28b4e7a90dc4db9bba18d1565e6c4824f0dc1b96b3b88d730da56e57, 0); } } // version history as per contractId // 0 - initial version // 1 - all ETO related terms dropped, fee disbursal recycle time added // 2 - method to calculate amount before token fee added /// @title sets terms of Platform contract PlatformTerms is Math, IContractId { //////////////////////// // Constants //////////////////////// // fraction of fee deduced on successful ETO (see Math.sol for fraction definition) uint256 public constant PLATFORM_FEE_FRACTION = 3 * 10**16; // fraction of tokens deduced on succesful ETO uint256 public constant TOKEN_PARTICIPATION_FEE_FRACTION = 2 * 10**16; // 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 public constant PLATFORM_NEUMARK_SHARE = 2; // 50:50 division // ICBM investors whitelisted by default bool public constant IS_ICBM_INVESTOR_WHITELISTED = true; // token rate expires after uint256 public constant TOKEN_RATE_EXPIRES_AFTER = 4 hours; // time after which claimable tokens become recycleable in fee disbursal pool uint256 public constant DEFAULT_DISBURSAL_RECYCLE_AFTER_DURATION = 4 * 365 days; //////////////////////// // Public Function //////////////////////// // calculates investor's and platform operator's neumarks from total reward function calculateNeumarkDistribution(uint256 rewardNmk) public pure 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); } // please note that this function and it's reverse calculateAmountWithoutFee will not produce exact reverse // values in each case due to rounding and that happens in cycle mod 51 for increasing values of tokenAmountWithFee // (frankly I'm not sure there are no more longer cycles, nothing in 50*51 cycle for sure which we checked) // so never rely in that in your code! // see ETOCommitment::onSigningTransition for example where it could lead to disastrous consequences function calculatePlatformTokenFee(uint256 tokenAmount) public pure returns (uint256) { // mind tokens having 0 precision // x*0.02 == x/50 return divRound(tokenAmount, 50); } // this calculates the amount before fee from the amount that already includes token fee function calculateAmountWithoutFee(uint256 tokenAmountWithFee) public pure returns (uint256) { // x + 0.02x = tokenAmount, x = tokenAmount * 1/1.02 = tokenAmount * 50 / 51 return divRound(mul(tokenAmountWithFee, 50), 51); } function calculatePlatformFee(uint256 amount) public pure returns (uint256) { return decimalFraction(amount, PLATFORM_FEE_FRACTION); } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x95482babc4e32de6c4dc3910ee7ae62c8e427efde6bc4e9ce0d6d93e24c39323, 2); } } /// @title sets duration of states in ETO contract ETODurationTerms is IContractId { //////////////////////// // Immutable state //////////////////////// // duration of Whitelist state uint32 public WHITELIST_DURATION; // duration of Public state uint32 public PUBLIC_DURATION; // time for Nominee and Company to sign Investment Agreement offchain and present proof on-chain uint32 public SIGNING_DURATION; // time for Claim before fee payout from ETO to NEU holders uint32 public CLAIM_DURATION; //////////////////////// // Constructor //////////////////////// constructor( uint32 whitelistDuration, uint32 publicDuration, uint32 signingDuration, uint32 claimDuration ) public { WHITELIST_DURATION = whitelistDuration; PUBLIC_DURATION = publicDuration; SIGNING_DURATION = signingDuration; CLAIM_DURATION = claimDuration; } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x5fb50201b453799d95f8a80291b940f1c543537b95bff2e3c78c2e36070494c0, 0); } } /// @title sets the contraints of the eto contract ETOTermsConstraints is IContractId { //////////////////////// // Types //////////////////////// enum OfferingDocumentType { Memorandum, Prospectus } enum OfferingDocumentSubType { Regular, Lean } enum AssetType { Security, VMA // Vermögensanlage } //////////////////////// // Immutable state //////////////////////// // min duration from setting the date to ETO start uint256 public constant DATE_TO_WHITELIST_MIN_DURATION = 7 days; // duration constraints uint256 public constant MIN_WHITELIST_DURATION = 0 days; uint256 public constant MAX_WHITELIST_DURATION = 30 days; uint256 public constant MIN_PUBLIC_DURATION = 0 days; uint256 public constant MAX_PUBLIC_DURATION = 60 days; // minimum length of whole offer uint256 public constant MIN_OFFER_DURATION = 1 days; // quarter should be enough for everyone uint256 public constant MAX_OFFER_DURATION = 90 days; uint256 public constant MIN_SIGNING_DURATION = 14 days; uint256 public constant MAX_SIGNING_DURATION = 60 days; uint256 public constant MIN_CLAIM_DURATION = 7 days; uint256 public constant MAX_CLAIM_DURATION = 30 days; // defines wether transfers are allowed after the eto ends bool public CAN_SET_TRANSFERABILITY; // defines wether a nominee is needed in the investment structure bool public HAS_NOMINEE; // minimum ticket size for this investment type uint256 public MIN_TICKET_SIZE_EUR_ULPS; // maximum ticket size for this investment type, 0 means unlimited uint256 public MAX_TICKET_SIZE_EUR_ULPS; // minimum total investment amount this investment type uint256 public MIN_INVESTMENT_AMOUNT_EUR_ULPS; // maximum total investment amount this investment type, 0 means unlimited uint256 public MAX_INVESTMENT_AMOUNT_EUR_ULPS; // public name string public NAME; // spec of the required offering document OfferingDocumentType public OFFERING_DOCUMENT_TYPE; OfferingDocumentSubType public OFFERING_DOCUMENT_SUB_TYPE; // jurisdiction in which the ETO will be conducted string public JURISDICTION; // legal type of asset that will be used AssetType public ASSET_TYPE; // address of the offering operator, will receive platform share from ETOCommitment address public TOKEN_OFFERING_OPERATOR; //////////////////////// // Constructor //////////////////////// constructor( bool canSetTransferability, bool hasNominee, uint256 minTicketSizeEurUlps, uint256 maxTicketSizeEurUlps, uint256 minInvestmentAmountEurUlps, uint256 maxInvestmentAmountEurUlps, string name, OfferingDocumentType offeringDocumentType, OfferingDocumentSubType offeringDocumentSubType, string jurisdiction, AssetType assetType, address tokenOfferingOperator ) public { require(maxTicketSizeEurUlps == 0 || minTicketSizeEurUlps<=maxTicketSizeEurUlps); require(maxInvestmentAmountEurUlps == 0 || minInvestmentAmountEurUlps<=maxInvestmentAmountEurUlps); require(maxInvestmentAmountEurUlps == 0 || minTicketSizeEurUlps<=maxInvestmentAmountEurUlps); require(assetType != AssetType.VMA || !canSetTransferability); require(tokenOfferingOperator != address(0x0)); CAN_SET_TRANSFERABILITY = canSetTransferability; HAS_NOMINEE = hasNominee; MIN_TICKET_SIZE_EUR_ULPS = minTicketSizeEurUlps; MAX_TICKET_SIZE_EUR_ULPS = maxTicketSizeEurUlps; MIN_INVESTMENT_AMOUNT_EUR_ULPS = minInvestmentAmountEurUlps; MAX_INVESTMENT_AMOUNT_EUR_ULPS = maxInvestmentAmountEurUlps; NAME = name; OFFERING_DOCUMENT_TYPE = offeringDocumentType; OFFERING_DOCUMENT_SUB_TYPE = offeringDocumentSubType; JURISDICTION = jurisdiction; ASSET_TYPE = assetType; TOKEN_OFFERING_OPERATOR = tokenOfferingOperator; } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0xce2be4f5f23c4a6f67ed925fce56afa57c9c8b274b4dfca8d0b1104aa4a6b53a, 0); } } // version history as per contract id // 0 - initial version // 1 - added SHARE_NOMINAL_VALUE_ULPS, SHARE_NOMINAL_VALUE_EUR_ULPS, TOKEN_NAME, TOKEN_SYMBOL, SHARE_PRICE /// @title sets terms for tokens in ETO contract ETOTokenTerms is Math, IContractId { //////////////////////// // Constants state //////////////////////// bytes32 private constant EMPTY_STRING_HASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // equity tokens decimals (precision) uint8 public constant EQUITY_TOKENS_PRECISION = 0; // indivisible //////////////////////// // Immutable state //////////////////////// // equity token metadata string public EQUITY_TOKEN_NAME; string public EQUITY_TOKEN_SYMBOL; // minimum number of tokens being offered. will set min cap uint256 public MIN_NUMBER_OF_TOKENS; // maximum number of tokens being offered. will set max cap uint256 public MAX_NUMBER_OF_TOKENS; // base token price in EUR-T, without any discount scheme uint256 public TOKEN_PRICE_EUR_ULPS; // maximum number of tokens in whitelist phase uint256 public MAX_NUMBER_OF_TOKENS_IN_WHITELIST; // sets nominal value of newly issued shares in currency of share capital as per ISHA // will be embedded in the equity token (IEquityToken interface) uint256 public SHARE_NOMINAL_VALUE_ULPS; // sets nominal value of newly issued shares in euro, used to withdraw share capital to Nominee uint256 public SHARE_NOMINAL_VALUE_EUR_ULPS; // equity tokens per share uint256 public EQUITY_TOKENS_PER_SHARE; //////////////////////// // Constructor //////////////////////// constructor( string equityTokenName, string equityTokenSymbol, uint256 minNumberOfTokens, uint256 maxNumberOfTokens, uint256 tokenPriceEurUlps, uint256 maxNumberOfTokensInWhitelist, uint256 shareNominalValueUlps, uint256 shareNominalValueEurUlps, uint256 equityTokensPerShare ) public { require(maxNumberOfTokens >= maxNumberOfTokensInWhitelist, "NF_WL_TOKENS_GT_MAX_TOKENS"); require(maxNumberOfTokens >= minNumberOfTokens, "NF_MIN_TOKENS_GT_MAX_TOKENS"); // min cap must be > single share require(minNumberOfTokens >= equityTokensPerShare, "NF_ETO_TERMS_ONE_SHARE"); // maximum number of tokens are full shares require(maxNumberOfTokens % equityTokensPerShare == 0, "NF_MAX_TOKENS_FULL_SHARES"); require(shareNominalValueUlps > 0); require(shareNominalValueEurUlps > 0); require(equityTokensPerShare > 0); require(keccak256(abi.encodePacked(equityTokenName)) != EMPTY_STRING_HASH); require(keccak256(abi.encodePacked(equityTokenSymbol)) != EMPTY_STRING_HASH); // overflows cannot be possible require(maxNumberOfTokens < 2**56, "NF_TOO_MANY_TOKENS"); require(mul(tokenPriceEurUlps, maxNumberOfTokens) < 2**112, "NF_TOO_MUCH_FUNDS_COLLECTED"); MIN_NUMBER_OF_TOKENS = minNumberOfTokens; MAX_NUMBER_OF_TOKENS = maxNumberOfTokens; TOKEN_PRICE_EUR_ULPS = tokenPriceEurUlps; MAX_NUMBER_OF_TOKENS_IN_WHITELIST = maxNumberOfTokensInWhitelist; SHARE_NOMINAL_VALUE_EUR_ULPS = shareNominalValueEurUlps; SHARE_NOMINAL_VALUE_ULPS = shareNominalValueUlps; EQUITY_TOKEN_NAME = equityTokenName; EQUITY_TOKEN_SYMBOL = equityTokenSymbol; EQUITY_TOKENS_PER_SHARE = equityTokensPerShare; } //////////////////////// // Public methods //////////////////////// function SHARE_PRICE_EUR_ULPS() public constant returns (uint256) { return mul(TOKEN_PRICE_EUR_ULPS, EQUITY_TOKENS_PER_SHARE); } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x591e791aab2b14c80194b729a2abcba3e8cce1918be4061be170e7223357ae5c, 1); } } // version history as per contract id // 0 - initial version // 1 - added ETOTermsConstraints to terms initialization // 2 - whitelist management shifted from company to WHITELIST ADMIN // 3 - SHARE_NOMINAL_VALUE_EUR_ULPS, TOKEN_NAME, TOKEN_SYMBOL moved to ETOTokenTerms // replaces EXISTING_COMPANY_SHARS with EXISTING_SHARE_CAPITAL, adds CURRENCY CODE // 4 - introduces // MAX_AVAILABLE_TOKENS with the actual amount of tokens for sale // MAX_AVAILABLE_TOKENS_IN_WHITELIST with the actual amount of tokens for sale in whitelist // ALLOWS_REGD_INVESTORS are US investors on reg-d allowed to participate in this ETO /// @title base terms of Equity Token Offering /// encapsulates pricing, discounts and whitelisting mechanism /// @dev to be split is mixins contract ETOTerms is AccessControlled, AccessRoles, IdentityRecord, Math, IContractId, KnownInterfaces { //////////////////////// // Types //////////////////////// // @notice whitelist entry with a discount struct WhitelistTicket { // this also overrides maximum ticket uint128 discountAmountEurUlps; // a percentage of full price to be paid (1 - discount) uint128 fullTokenPriceFrac; } //////////////////////// // Constants state //////////////////////// bytes32 private constant EMPTY_STRING_HASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; //////////////////////// // Immutable state //////////////////////// // reference to duration terms ETODurationTerms public DURATION_TERMS; // reference to token terms ETOTokenTerms public TOKEN_TERMS; // currency code in which share capital is provided string public SHARE_CAPITAL_CURRENCY_CODE; // shares capital in ISHA currency at the beginning of the sale, excl. Authorized Capital uint256 public EXISTING_SHARE_CAPITAL; // maximum discount on token price that may be given to investor (as decimal fraction) // uint256 public MAXIMUM_TOKEN_PRICE_DISCOUNT_FRAC; // minimum ticket uint256 public MIN_TICKET_EUR_ULPS; // maximum ticket, is never 0, will be set to maximum possible cap to reduce number of conditions later uint256 public MAX_TICKET_EUR_ULPS; // should enable transfers on ETO success // transfers are always disabled during token offering // if set to False transfers on Equity Token will remain disabled after offering // once those terms are on-chain this flags fully controls token transferability bool public ENABLE_TRANSFERS_ON_SUCCESS; // represents the discount % for whitelist participants uint256 public WHITELIST_DISCOUNT_FRAC; // represents the discount % for public participants, using values > 0 will result // in automatic downround shareholder resolution uint256 public PUBLIC_DISCOUNT_FRAC; // tells is RegD US investors are allowed to participate uint256 public ALLOWS_REGD_INVESTORS; // paperwork // prospectus / investment memorandum / crowdfunding pamphlet etc. string public INVESTOR_OFFERING_DOCUMENT_URL; // settings for shareholder rights ShareholderRights public SHAREHOLDER_RIGHTS; // wallet registry of KYC procedure IIdentityRegistry public IDENTITY_REGISTRY; Universe public UNIVERSE; // terms constraints (a.k.a. "Product") ETOTermsConstraints public ETO_TERMS_CONSTRAINTS; // number of tokens that can be sold, + 2% = MAX_NUMBER_OF_TOKENS uint256 public MAX_AVAILABLE_TOKENS; // number of tokens that can be sold in whitelist uint256 public MAX_AVAILABLE_TOKENS_IN_WHITELIST; // base token price in EUR-T, without any discount scheme uint256 private TOKEN_PRICE_EUR_ULPS; // equity tokens per share uint256 private EQUITY_TOKENS_PER_SHARE; //////////////////////// // Mutable state //////////////////////// // mapping of investors allowed in whitelist mapping (address => WhitelistTicket) private _whitelist; //////////////////////// // Events //////////////////////// // raised on invesor added to whitelist event LogInvestorWhitelisted( address indexed investor, uint256 discountAmountEurUlps, uint256 fullTokenPriceFrac ); //////////////////////// // Constructor //////////////////////// constructor( Universe universe, ETODurationTerms durationTerms, ETOTokenTerms tokenTerms, string shareCapitalCurrencyCode, uint256 existingShareCapital, uint256 minTicketEurUlps, uint256 maxTicketEurUlps, bool enableTransfersOnSuccess, string investorOfferingDocumentUrl, ShareholderRights shareholderRights, uint256 whitelistDiscountFrac, uint256 publicDiscountFrac, ETOTermsConstraints etoTermsConstraints ) AccessControlled(universe.accessPolicy()) public { require(durationTerms != address(0)); require(tokenTerms != address(0)); require(existingShareCapital > 0); require(keccak256(abi.encodePacked(investorOfferingDocumentUrl)) != EMPTY_STRING_HASH); require(keccak256(abi.encodePacked(shareCapitalCurrencyCode)) != EMPTY_STRING_HASH); require(shareholderRights != address(0)); // test interface require(shareholderRights.HAS_GENERAL_INFORMATION_RIGHTS()); require(whitelistDiscountFrac >= 0 && whitelistDiscountFrac <= 99*10**16, "NF_DISCOUNT_RANGE"); require(publicDiscountFrac >= 0 && publicDiscountFrac <= 99*10**16, "NF_DISCOUNT_RANGE"); require(minTicketEurUlps<=maxTicketEurUlps); require(tokenTerms.EQUITY_TOKENS_PRECISION() == 0); require(universe.isInterfaceCollectionInstance(KNOWN_INTERFACE_ETO_TERMS_CONSTRAINTS, etoTermsConstraints), "NF_TERMS_NOT_IN_UNIVERSE"); // save reference to constraints ETO_TERMS_CONSTRAINTS = etoTermsConstraints; // copy token terms variables TOKEN_PRICE_EUR_ULPS = tokenTerms.TOKEN_PRICE_EUR_ULPS(); EQUITY_TOKENS_PER_SHARE = tokenTerms.EQUITY_TOKENS_PER_SHARE(); DURATION_TERMS = durationTerms; TOKEN_TERMS = tokenTerms; SHARE_CAPITAL_CURRENCY_CODE = shareCapitalCurrencyCode; EXISTING_SHARE_CAPITAL = existingShareCapital; MIN_TICKET_EUR_ULPS = minTicketEurUlps; MAX_TICKET_EUR_ULPS = maxTicketEurUlps; ENABLE_TRANSFERS_ON_SUCCESS = enableTransfersOnSuccess; INVESTOR_OFFERING_DOCUMENT_URL = investorOfferingDocumentUrl; SHAREHOLDER_RIGHTS = shareholderRights; WHITELIST_DISCOUNT_FRAC = whitelistDiscountFrac; PUBLIC_DISCOUNT_FRAC = publicDiscountFrac; IDENTITY_REGISTRY = IIdentityRegistry(universe.identityRegistry()); UNIVERSE = universe; // compute max available tokens to be sold in ETO MAX_AVAILABLE_TOKENS = calculateAvailableTokens(tokenTerms.MAX_NUMBER_OF_TOKENS()); MAX_AVAILABLE_TOKENS_IN_WHITELIST = min(MAX_AVAILABLE_TOKENS, tokenTerms.MAX_NUMBER_OF_TOKENS_IN_WHITELIST()); // validate all settings requireValidTerms(); } //////////////////////// // Public methods //////////////////////// // calculates token amount for a given commitment at a position of the curve // we require that equity token precision is 0 function calculateTokenAmount(uint256 /*totalEurUlps*/, uint256 committedEurUlps) public constant returns (uint256 tokenAmountInt) { // we may disregard totalEurUlps as curve is flat, round down when calculating tokens return committedEurUlps / calculatePriceFraction(10**18 - PUBLIC_DISCOUNT_FRAC); } // calculates amount of euro required to acquire amount of tokens at a position of the (inverse) curve // we require that equity token precision is 0 function calculateEurUlpsAmount(uint256 /*totalTokensInt*/, uint256 tokenAmountInt) public constant returns (uint256 committedEurUlps) { // we may disregard totalTokensInt as curve is flat return mul(tokenAmountInt, calculatePriceFraction(10**18 - PUBLIC_DISCOUNT_FRAC)); } function calculatePriceFraction(uint256 priceFrac) public constant returns(uint256) { if (priceFrac == 1) { return TOKEN_PRICE_EUR_ULPS; } else { return decimalFraction(priceFrac, TOKEN_PRICE_EUR_ULPS); } } /// @notice returns number of shares as a decimal fraction function equityTokensToShares(uint256 amount) public constant returns (uint256) { return proportion(amount, 10**18, EQUITY_TOKENS_PER_SHARE); } function addWhitelisted( address[] investors, uint256[] discountAmountsEurUlps, uint256[] discountsFrac ) external only(ROLE_WHITELIST_ADMIN) { require(investors.length == discountAmountsEurUlps.length); require(investors.length == discountsFrac.length); for (uint256 i = 0; i < investors.length; i += 1) { addWhitelistInvestorPrivate(investors[i], discountAmountsEurUlps[i], discountsFrac[i]); } } function whitelistTicket(address investor) public constant returns (bool isWhitelisted, uint256 discountAmountEurUlps, uint256 fullTokenPriceFrac) { WhitelistTicket storage wlTicket = _whitelist[investor]; isWhitelisted = wlTicket.fullTokenPriceFrac > 0; discountAmountEurUlps = wlTicket.discountAmountEurUlps; fullTokenPriceFrac = wlTicket.fullTokenPriceFrac; } // calculate contribution of investor function calculateContribution( address investor, uint256 totalContributedEurUlps, uint256 existingInvestorContributionEurUlps, uint256 newInvestorContributionEurUlps, bool applyWhitelistDiscounts ) public constant returns ( bool isWhitelisted, bool isEligible, uint256 minTicketEurUlps, uint256 maxTicketEurUlps, uint256 equityTokenInt, uint256 fixedSlotEquityTokenInt ) { ( isWhitelisted, minTicketEurUlps, maxTicketEurUlps, equityTokenInt, fixedSlotEquityTokenInt ) = calculateContributionPrivate( investor, totalContributedEurUlps, existingInvestorContributionEurUlps, newInvestorContributionEurUlps, applyWhitelistDiscounts); // check if is eligible for investment IdentityClaims memory claims = deserializeClaims(IDENTITY_REGISTRY.getClaims(investor)); // use simple formula to disallow us accredited investors isEligible = claims.isVerified && !claims.accountFrozen && !claims.requiresRegDAccreditation; } /// @notice checks terms against terms constraints, reverts on invalid function requireValidTerms() public constant returns (bool) { // available tokens >= MIN AVAIABLE TOKENS uint256 minTokens = TOKEN_TERMS.MIN_NUMBER_OF_TOKENS(); require(MAX_AVAILABLE_TOKENS >= minTokens, "NF_AVAILABLE_TOKEN_LT_MIN_TOKENS"); // min ticket must be > token price require(MIN_TICKET_EUR_ULPS >= TOKEN_TERMS.TOKEN_PRICE_EUR_ULPS(), "NF_MIN_TICKET_LT_TOKEN_PRICE"); // it must be possible to collect more funds than max number of tokens uint256 estimatedMaxCap = calculateEurUlpsAmount(0, MAX_AVAILABLE_TOKENS); require(estimatedMaxCap >= MIN_TICKET_EUR_ULPS, "NF_MAX_FUNDS_LT_MIN_TICKET"); // min cap must be less than MAX_CAP product limit, otherwise ETO always refunds uint256 constraintsMaxInvestment = ETO_TERMS_CONSTRAINTS.MAX_INVESTMENT_AMOUNT_EUR_ULPS(); uint256 estimatedMinCap = calculateEurUlpsAmount(0, minTokens); require(constraintsMaxInvestment == 0 || estimatedMinCap <= constraintsMaxInvestment, "NF_MIN_CAP_GT_PROD_MAX_CAP"); // ticket size checks require(MIN_TICKET_EUR_ULPS >= ETO_TERMS_CONSTRAINTS.MIN_TICKET_SIZE_EUR_ULPS(), "NF_ETO_TERMS_MIN_TICKET_EUR_ULPS"); uint256 constraintsMaxTicket = ETO_TERMS_CONSTRAINTS.MAX_TICKET_SIZE_EUR_ULPS(); require( constraintsMaxTicket == 0 || // unlimited investment allowed (MAX_TICKET_EUR_ULPS <= constraintsMaxTicket), // or max ticket of eto is NOT unlimited and lte the terms allow "NF_ETO_TERMS_MAX_TICKET_EUR_ULPS" ); // only allow transferabilty if this is allowed in general require(!ENABLE_TRANSFERS_ON_SUCCESS || ETO_TERMS_CONSTRAINTS.CAN_SET_TRANSFERABILITY(), "NF_ETO_TERMS_ENABLE_TRANSFERS_ON_SUCCESS"); // duration checks require(DURATION_TERMS.WHITELIST_DURATION() >= ETO_TERMS_CONSTRAINTS.MIN_WHITELIST_DURATION(), "NF_ETO_TERMS_WL_D_MIN"); require(DURATION_TERMS.WHITELIST_DURATION() <= ETO_TERMS_CONSTRAINTS.MAX_WHITELIST_DURATION(), "NF_ETO_TERMS_WL_D_MAX"); require(DURATION_TERMS.PUBLIC_DURATION() >= ETO_TERMS_CONSTRAINTS.MIN_PUBLIC_DURATION(), "NF_ETO_TERMS_PUB_D_MIN"); require(DURATION_TERMS.PUBLIC_DURATION() <= ETO_TERMS_CONSTRAINTS.MAX_PUBLIC_DURATION(), "NF_ETO_TERMS_PUB_D_MAX"); uint256 totalDuration = DURATION_TERMS.WHITELIST_DURATION() + DURATION_TERMS.PUBLIC_DURATION(); require(totalDuration >= ETO_TERMS_CONSTRAINTS.MIN_OFFER_DURATION(), "NF_ETO_TERMS_TOT_O_MIN"); require(totalDuration <= ETO_TERMS_CONSTRAINTS.MAX_OFFER_DURATION(), "NF_ETO_TERMS_TOT_O_MAX"); require(DURATION_TERMS.SIGNING_DURATION() >= ETO_TERMS_CONSTRAINTS.MIN_SIGNING_DURATION(), "NF_ETO_TERMS_SIG_MIN"); require(DURATION_TERMS.SIGNING_DURATION() <= ETO_TERMS_CONSTRAINTS.MAX_SIGNING_DURATION(), "NF_ETO_TERMS_SIG_MAX"); require(DURATION_TERMS.CLAIM_DURATION() >= ETO_TERMS_CONSTRAINTS.MIN_CLAIM_DURATION(), "NF_ETO_TERMS_CLAIM_MIN"); require(DURATION_TERMS.CLAIM_DURATION() <= ETO_TERMS_CONSTRAINTS.MAX_CLAIM_DURATION(), "NF_ETO_TERMS_CLAIM_MAX"); return true; } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x3468b14073c33fa00ee7f8a289b14f4a10c78ab72726033b27003c31c47b3f6a, 3); } //////////////////////// // Private methods //////////////////////// function calculateContributionPrivate( address investor, uint256 totalContributedEurUlps, uint256 existingInvestorContributionEurUlps, uint256 newInvestorContributionEurUlps, bool applyWhitelistDiscounts ) private constant returns ( bool isWhitelisted, uint256 minTicketEurUlps, uint256 maxTicketEurUlps, uint256 equityTokenInt, uint256 fixedSlotEquityTokenInt ) { uint256 discountedAmount; minTicketEurUlps = MIN_TICKET_EUR_ULPS; maxTicketEurUlps = MAX_TICKET_EUR_ULPS; WhitelistTicket storage wlTicket = _whitelist[investor]; // check if has access to discount isWhitelisted = wlTicket.fullTokenPriceFrac > 0; // whitelist use discount is possible if (applyWhitelistDiscounts) { // can invest more than general max ticket maxTicketEurUlps = max(wlTicket.discountAmountEurUlps, maxTicketEurUlps); // can invest less than general min ticket if (wlTicket.discountAmountEurUlps > 0) { minTicketEurUlps = min(wlTicket.discountAmountEurUlps, minTicketEurUlps); } if (existingInvestorContributionEurUlps < wlTicket.discountAmountEurUlps) { discountedAmount = min(newInvestorContributionEurUlps, wlTicket.discountAmountEurUlps - existingInvestorContributionEurUlps); // discount is fixed so use base token price if (discountedAmount > 0) { // always round down when calculating tokens fixedSlotEquityTokenInt = discountedAmount / calculatePriceFraction(wlTicket.fullTokenPriceFrac); // todo: compute effective amount spent without the rounding // discountAmount = fixedSlotEquityTokenInt * calculatePriceFraction(wlTicket.fullTokenPriceFrac); } } } // if any amount above discount uint256 remainingAmount = newInvestorContributionEurUlps - discountedAmount; if (remainingAmount > 0) { if (applyWhitelistDiscounts && WHITELIST_DISCOUNT_FRAC > 0) { // will not overflow, WHITELIST_DISCOUNT_FRAC < Q18 from constructor, also round down equityTokenInt = remainingAmount / calculatePriceFraction(10**18 - WHITELIST_DISCOUNT_FRAC); // todo: compute effective amount spent without the rounding // remainingAmount = equityTokenInt * calculatePriceFraction(10**18 - WHITELIST_DISCOUNT_FRAC); } else { // use pricing along the curve equityTokenInt = calculateTokenAmount(totalContributedEurUlps + discountedAmount, remainingAmount); // todo: remove function above and calculate directly // remainingAmount = equityTokenInt * fullPrice; } } // should have all issued tokens equityTokenInt += fixedSlotEquityTokenInt; // todo: return remainingAmount as effective amount spent for the least gas used } function addWhitelistInvestorPrivate( address investor, uint256 discountAmountEurUlps, uint256 fullTokenPriceFrac ) private { require(investor != address(0)); // allow full token price and discount amount to be both 0 to allow deletions require((fullTokenPriceFrac > 0 || discountAmountEurUlps == 0) && fullTokenPriceFrac <= 10**18, "NF_DISCOUNT_RANGE"); require(discountAmountEurUlps < 2**128); _whitelist[investor] = WhitelistTicket({ discountAmountEurUlps: uint128(discountAmountEurUlps), fullTokenPriceFrac: uint128(fullTokenPriceFrac) }); emit LogInvestorWhitelisted(investor, discountAmountEurUlps, fullTokenPriceFrac); } function calculateAvailableTokens(uint256 amountWithFee) private constant returns (uint256) { return PlatformTerms(UNIVERSE.platformTerms()).calculateAmountWithoutFee(amountWithFee); } } /// @title default interface of commitment process /// investment always happens via payment token ERC223 callback /// methods for checking finality and success/fail of the process are vailable /// commitment event is standardized for tracking contract ICommitment is IAgreement, IERC223Callback { //////////////////////// // Events //////////////////////// /// on every commitment transaction /// `investor` committed `amount` in `paymentToken` currency which was /// converted to `baseCurrencyEquivalent` that generates `grantedAmount` of /// `assetToken` and `neuReward` NEU /// for investment funds could be provided from `wallet` (like icbm wallet) controlled by investor event LogFundsCommitted( address indexed investor, address wallet, address paymentToken, uint256 amount, uint256 baseCurrencyEquivalent, uint256 grantedAmount, address assetToken, uint256 neuReward ); //////////////////////// // Public functions //////////////////////// // says if state is final function finalized() public constant returns (bool); // says if state is success function success() public constant returns (bool); // says if state is failure function failed() public constant returns (bool); // currently committed funds function totalInvestment() public constant returns ( uint256 totalEquivEurUlps, uint256 totalTokensInt, uint256 totalInvestors ); /// commit function happens via ERC223 callback that must happen from trusted payment token /// @param investor address of the investor /// @param amount amount commited /// @param data may have meaning in particular ETO implementation function tokenFallback(address investor, uint256 amount, bytes data) public; } /// @title default interface of commitment process contract IETOCommitment is ICommitment, IETOCommitmentStates { //////////////////////// // Events //////////////////////// // on every state transition event LogStateTransition( uint32 oldState, uint32 newState, uint32 timestamp ); /// on a claim by invester /// `investor` claimed `amount` of `assetToken` and claimed `nmkReward` amount of NEU event LogTokensClaimed( address indexed investor, address indexed assetToken, uint256 amount, uint256 nmkReward ); /// on a refund to investor /// `investor` was refunded `amount` of `paymentToken` /// @dev may be raised multiple times per refund operation event LogFundsRefunded( address indexed investor, address indexed paymentToken, uint256 amount ); // logged at the moment of Company setting terms event LogTermsSet( address companyLegalRep, address etoTerms, address equityToken ); // logged at the moment Company sets/resets Whitelisting start date event LogETOStartDateSet( address companyLegalRep, uint256 previousTimestamp, uint256 newTimestamp ); // logged at the moment Signing procedure starts event LogSigningStarted( address nominee, address companyLegalRep, uint256 newShares, uint256 capitalIncreaseUlps ); // logged when company presents signed investment agreement event LogCompanySignedAgreement( address companyLegalRep, address nominee, string signedInvestmentAgreementUrl ); // logged when nominee presents and verifies its copy of investment agreement event LogNomineeConfirmedAgreement( address nominee, address companyLegalRep, string signedInvestmentAgreementUrl ); // logged on refund transition to mark destroyed tokens event LogRefundStarted( address assetToken, uint256 totalTokenAmountInt, uint256 totalRewardNmkUlps ); //////////////////////// // Public functions //////////////////////// // // ETOState control // // returns current ETO state function state() public constant returns (ETOState); // returns start of given state function startOf(ETOState s) public constant returns (uint256); // returns commitment observer (typically equity token controller) function commitmentObserver() public constant returns (IETOCommitmentObserver); // // Commitment process // /// refunds investor if ETO failed function refund() external; /// claims tokens if ETO is a success function claim() external; // initiate fees payout function payout() external; // // Offering terms // function etoTerms() public constant returns (ETOTerms); // equity token function equityToken() public constant returns (IEquityToken); // nominee function nominee() public constant returns (address); function companyLegalRep() public constant returns (address); /// signed agreement as provided by company and nominee /// is final in Claim and Payout states, may change at any moment in Signing state function signedInvestmentAgreementUrl() public constant returns (string); /// financial outcome of token offering set on Signing state transition /// @dev in preceding states 0 is returned function contributionSummary() public constant returns ( uint256 newShares, uint256 capitalIncreaseEurUlps, uint256 additionalContributionEth, uint256 additionalContributionEurUlps, uint256 tokenParticipationFeeInt, uint256 platformFeeEth, uint256 platformFeeEurUlps, uint256 sharePriceEurUlps ); /// method to obtain current investors ticket function investorTicket(address investor) public constant returns ( uint256 equivEurUlps, uint256 rewardNmkUlps, uint256 equityTokenInt, uint256 sharesInt, uint256 tokenPrice, uint256 neuRate, uint256 amountEth, uint256 amountEurUlps, bool claimOrRefundSettled, bool usedLockedAccount ); } contract METOStateMachineObserver is IETOCommitmentStates { /// @notice called before state transitions, allows override transition due to additional business logic /// @dev advance due to time implemented in advanceTimedState, here implement other conditions like /// max cap reached -> we go to signing function mBeforeStateTransition(ETOState oldState, ETOState newState) internal constant returns (ETOState newStateOverride); /// @notice gets called after every state transition. function mAfterTransition(ETOState oldState, ETOState newState) internal; /// @notice gets called after business logic, may induce state transition function mAdavanceLogicState(ETOState oldState) internal constant returns (ETOState); } /// @title time induced state machine for Equity Token Offering /// @notice implements ETO state machine with setup, whitelist, public, signing, claim, refund and payout phases /// @dev inherited contract must implement internal interface, see comments /// intended usage via 'withStateTransition' 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 timed state change request is publicly accessible via 'handleTimedTransitions' /// @dev time is based on block.timestamp contract ETOTimedStateMachine is IETOCommitment, METOStateMachineObserver { //////////////////////// // CONSTANTS //////////////////////// // uint32 private constant TS_STATE_NOT_SET = 1; //////////////////////// // Immutable state //////////////////////// // maps states to durations (index is ETOState) uint32[] private ETO_STATE_DURATIONS; // observer receives notifications on all state changes IETOCommitmentObserver private COMMITMENT_OBSERVER; //////////////////////// // Mutable state //////////////////////// // current state ETOState private _state = ETOState.Setup; // historical times of state transition (index is ETOState) // internal access used to allow mocking time uint32[7] internal _pastStateTransitionTimes; //////////////////////// // Modifiers //////////////////////// // @dev This modifier needs to be applied to all external non-constant functions. // this modifier goes _before_ other state modifiers like `onlyState`. // after function body execution state may transition again in `advanceLogicState` modifier withStateTransition() { // switch state due to time advanceTimedState(); // execute function body _; // switch state due to business logic advanceLogicState(); } modifier onlyState(ETOState state) { require(_state == state); _; } modifier onlyStates(ETOState state0, ETOState state1) { require(_state == state0 || _state == state1); _; } /// @dev Multiple states can be handled by adding more modifiers. /* modifier notInState(ETOState state) { require(_state != state); _; }*/ //////////////////////// // Public functions //////////////////////// // @notice This function is public so that it can be called independently. function handleStateTransitions() public { advanceTimedState(); } // // Implements ICommitment // // says if state is final function finalized() public constant returns (bool) { return (_state == ETOState.Refund || _state == ETOState.Payout || _state == ETOState.Claim); } // says if state is success function success() public constant returns (bool) { return (_state == ETOState.Claim || _state == ETOState.Payout); } // says if state is filure function failed() public constant returns (bool) { return _state == ETOState.Refund; } // // Implement IETOCommitment // function state() public constant returns (ETOState) { return _state; } function startOf(ETOState s) public constant returns (uint256) { return startOfInternal(s); } // returns time induced state which differs from storage state if transition is overdue function timedState() external constant returns (ETOState) { // below we change state but function is constant. the intention is to force this function to be eth_called advanceTimedState(); return _state; } function startOfStates() public constant returns (uint256[7] startOfs) { // 7 is number of states for(uint256 ii = 0;ii<ETO_STATES_COUNT;ii += 1) { startOfs[ii] = startOfInternal(ETOState(ii)); } } function commitmentObserver() public constant returns (IETOCommitmentObserver) { return COMMITMENT_OBSERVER; } //////////////////////// // Internal functions //////////////////////// function setupStateMachine(ETODurationTerms durationTerms, IETOCommitmentObserver observer) internal { require(COMMITMENT_OBSERVER == address(0), "NF_STM_SET_ONCE"); require(observer != address(0)); COMMITMENT_OBSERVER = observer; ETO_STATE_DURATIONS = [ 0, durationTerms.WHITELIST_DURATION(), durationTerms.PUBLIC_DURATION(), durationTerms.SIGNING_DURATION(), durationTerms.CLAIM_DURATION(), 0, 0 ]; } function runStateMachine(uint32 startDate) internal { // this sets expiration of setup state _pastStateTransitionTimes[uint32(ETOState.Setup)] = startDate; } function startOfInternal(ETOState s) internal constant returns (uint256) { // initial state does not have start time if (s == ETOState.Setup) { return 0; } // if timed state machine was not run, the next state will never come // if (_pastStateTransitionTimes[uint32(ETOState.Setup)] == 0) { // return 0xFFFFFFFF; // } // special case for Refund if (s == ETOState.Refund) { return _state == s ? _pastStateTransitionTimes[uint32(_state)] : 0; } // current and previous states: just take s - 1 which is the end of previous state if (uint32(s) - 1 <= uint32(_state)) { return _pastStateTransitionTimes[uint32(s) - 1]; } // for future states uint256 currStateExpiration = _pastStateTransitionTimes[uint32(_state)]; // this trick gets start of required state by adding all durations between current and required states // note that past and current state were handled above so required state is in the future // we also rely on terminal states having duration of 0 for (uint256 stateIdx = uint32(_state) + 1; stateIdx < uint32(s); stateIdx++) { currStateExpiration += ETO_STATE_DURATIONS[stateIdx]; } return currStateExpiration; } //////////////////////// // Private functions //////////////////////// // @notice time induced state transitions, called before logic // @dev don't use `else if` and keep sorted by time and call `state()` // or else multiple transitions won't cascade properly. function advanceTimedState() private { // if timed state machine was not run, the next state will never come if (_pastStateTransitionTimes[uint32(ETOState.Setup)] == 0) { return; } uint256 t = block.timestamp; if (_state == ETOState.Setup && t >= startOfInternal(ETOState.Whitelist)) { transitionTo(ETOState.Whitelist); } if (_state == ETOState.Whitelist && t >= startOfInternal(ETOState.Public)) { transitionTo(ETOState.Public); } if (_state == ETOState.Public && t >= startOfInternal(ETOState.Signing)) { transitionTo(ETOState.Signing); } // signing to refund: first we check if it's claim time and if it we go // for refund. to go to claim agreement MUST be signed, no time transition if (_state == ETOState.Signing && t >= startOfInternal(ETOState.Claim)) { transitionTo(ETOState.Refund); } // claim to payout if (_state == ETOState.Claim && t >= startOfInternal(ETOState.Payout)) { transitionTo(ETOState.Payout); } } // @notice transitions due to business logic // @dev called after logic function advanceLogicState() private { ETOState newState = mAdavanceLogicState(_state); if (_state != newState) { transitionTo(newState); // if we had state transition, we may have another advanceLogicState(); } } /// @notice executes transition state function function transitionTo(ETOState newState) private { ETOState oldState = _state; ETOState effectiveNewState = mBeforeStateTransition(oldState, newState); // require(validTransition(oldState, effectiveNewState)); _state = effectiveNewState; // store deadline for previous state uint32 deadline = _pastStateTransitionTimes[uint256(oldState)]; // if transition came before deadline, count time from timestamp, if after always count from deadline if (uint32(block.timestamp) < deadline) { deadline = uint32(block.timestamp); } // we have 60+ years for 2^32 overflow on epoch so disregard _pastStateTransitionTimes[uint256(oldState)] = deadline; // set deadline on next state _pastStateTransitionTimes[uint256(effectiveNewState)] = deadline + ETO_STATE_DURATIONS[uint256(effectiveNewState)]; // should not change _state mAfterTransition(oldState, effectiveNewState); assert(_state == effectiveNewState); // should notify observer after internal state is settled COMMITMENT_OBSERVER.onStateTransition(oldState, effectiveNewState); emit LogStateTransition(uint32(oldState), uint32(effectiveNewState), deadline); } /*function validTransition(ETOState oldState, ETOState newState) private pure returns (bool valid) { // TODO: think about disabling it before production deployment // (oldState == ETOState.Setup && newState == ETOState.Public) || // (oldState == ETOState.Setup && newState == ETOState.Refund) || return (oldState == ETOState.Setup && newState == ETOState.Whitelist) || (oldState == ETOState.Whitelist && newState == ETOState.Public) || (oldState == ETOState.Whitelist && newState == ETOState.Signing) || (oldState == ETOState.Public && newState == ETOState.Signing) || (oldState == ETOState.Public && newState == ETOState.Refund) || (oldState == ETOState.Signing && newState == ETOState.Refund) || (oldState == ETOState.Signing && newState == ETOState.Claim) || (oldState == ETOState.Claim && newState == ETOState.Payout); }*/ } // version history as per contractId // 0 - initial version used to deploy FF ETO // 1 - a refactor that includes (20.05.2019) // - removes PlatformTerms and introduces ETOTermsContraints (aka Product) // - introduces token offering operator instead of PLATFORM_WALLET // - present in singletons() method replacing PLATFORM_WALLET // 2 - version with recycle method added and claimMany, refundMany removed (08.06.2019) // 3 - capitalIncrease returned in ISHA currency, ABI and return values backward compatible // calculateContribution returns actually spent amount at index 7 /// @title represents token offering organized by Company /// token offering goes through states as defined in ETOTimedStateMachine /// setup phase requires several parties to provide documents and information /// (deployment (by anyone) -> eto terms (company) -> RAAA agreement (nominee) -> adding to universe (platform) + issue NEU -> start date (company)) /// price curves, whitelists, discounts and other offer terms are extracted to ETOTerms /// todo: review all divisions for rounding errors contract ETOCommitment is AccessControlled, Agreement, ETOTimedStateMachine, Math, Serialization, IContractId, KnownInterfaces { //////////////////////// // Types //////////////////////// /// @notice state of individual investment /// @dev mind uint size: allows ticket to occupy two storage slots struct InvestmentTicket { // euro equivalent of both currencies. // for ether equivalent is generated per ETH/EUR spot price provided by ITokenExchangeRateOracle uint96 equivEurUlps; // NEU reward issued uint96 rewardNmkUlps; // Equity Tokens issued, no precision uint96 equityTokenInt; // total Ether invested uint96 amountEth; // total Euro invested uint96 amountEurUlps; // claimed or refunded bool claimOrRefundSettled; // locked account was used bool usedLockedAccount; // uint30 reserved // still some bits free } //////////////////////// // Immutable state //////////////////////// // a root of trust contract Universe private UNIVERSE; // NEU tokens issued as reward for investment Neumark private NEUMARK; // ether token to store and transfer ether IERC223Token private ETHER_TOKEN; // euro token to store and transfer euro IERC223Token private EURO_TOKEN; // allowed icbm investor accounts LockedAccount private ETHER_LOCK; LockedAccount private EURO_LOCK; // equity token issued IEquityToken private EQUITY_TOKEN; // currency rate oracle ITokenExchangeRateOracle private CURRENCY_RATES; // max cap taken from ETOTerms for low gas costs uint256 private MIN_NUMBER_OF_TOKENS; // maximum tokens we can sell in this ETO, value after token fee was subtracted from MAX TOK CAP uint256 private MAX_AVAILABLE_TOKENS; // max cap of tokens in whitelist (without fixed slots) uint256 private MAX_AVAILABLE_TOKENS_IN_WHITELIST; // minimum ticket in tokens with base price uint256 private MIN_TICKET_TOKENS; // platform operator share for low gas costs uint128 private PLATFORM_NEUMARK_SHARE; // token rate expires after uint128 private TOKEN_RATE_EXPIRES_AFTER; // max investment amount uint256 private MAX_INVESTMENT_AMOUNT_EUR_ULPS; // min ticket size, taken from eto terms uint256 private MIN_TICKET_EUR_ULPS; // company representative address address private COMPANY_LEGAL_REPRESENTATIVE; // nominee address address private NOMINEE; // terms contracts ETOTerms private ETO_TERMS; // terms constraints (a.k.a. "Product") ETOTermsConstraints public ETO_TERMS_CONSTRAINTS; // reference to platform terms PlatformTerms private PLATFORM_TERMS; // reference to the offering operator address public TOKEN_OFFERING_OPERATOR; //////////////////////// // Mutable state //////////////////////// // investment tickets mapping (address => InvestmentTicket) private _tickets; // data below start at 32 bytes boundary and pack into 32 bytes word // total investment in euro equivalent (ETH converted on spot prices) uint112 private _totalEquivEurUlps; // total equity tokens acquired uint56 private _totalTokensInt; // total equity tokens acquired in fixed slots uint56 private _totalFixedSlotsTokensInt; // total investors that participated uint32 private _totalInvestors; // nominee investment agreement url confirmation hash bytes32 private _nomineeSignedInvestmentAgreementUrlHash; // additonal contribution / investment amount eth // it holds investment eth amount until end of public phase, then additional contribution uint112 private _additionalContributionEth; // additonal contribution / investment amount eur // it holds investment eur amount until end of public phase, then additional contribution uint112 private _additionalContributionEurUlps; // successful ETO bookeeping // amount of new shares generated never exceeds number of tokens (uint56) uint64 private _newShares; // how many equity tokens goes to platform portfolio as a fee uint64 private _tokenParticipationFeeInt; // platform fee in eth uint112 private _platformFeeEth; // platform fee in eur uint112 private _platformFeeEurUlps; // signed investment agreement url string private _signedInvestmentAgreementUrl; //////////////////////// // Modifiers //////////////////////// modifier onlyCompany() { require(msg.sender == COMPANY_LEGAL_REPRESENTATIVE); _; } modifier onlyNominee() { require(msg.sender == NOMINEE); _; } modifier onlyWithAgreement { require(amendmentsCount() > 0); _; } //////////////////////// // Events //////////////////////// // logged on claim state transition indicating that additional contribution was released to company event LogAdditionalContribution( address companyLegalRep, address paymentToken, uint256 amount ); // logged on claim state transition indicating NEU reward available event LogPlatformNeuReward( address tokenOfferingOperator, uint256 totalRewardNmkUlps, uint256 platformRewardNmkUlps ); // logged on payout transition to mark cash payout to NEU holders event LogPlatformFeePayout( address paymentToken, address disbursalPool, uint256 amount ); // logged on payout transition to mark equity token payout to portfolio smart contract event LogPlatformPortfolioPayout( address assetToken, address platformPortfolio, uint256 amount ); //////////////////////// // Constructor //////////////////////// /// anyone may be a deployer, the platform acknowledges the contract by adding it to Universe Commitment collection constructor( Universe universe, address nominee, address companyLegalRep, ETOTerms etoTerms, IEquityToken equityToken ) Agreement(universe.accessPolicy(), universe.forkArbiter()) ETOTimedStateMachine() public { UNIVERSE = universe; PLATFORM_TERMS = PlatformTerms(universe.platformTerms()); require(equityToken.decimals() == etoTerms.TOKEN_TERMS().EQUITY_TOKENS_PRECISION()); require(nominee != address(0) && companyLegalRep != address(0)); ETO_TERMS_CONSTRAINTS = etoTerms.ETO_TERMS_CONSTRAINTS(); TOKEN_OFFERING_OPERATOR = ETO_TERMS_CONSTRAINTS.TOKEN_OFFERING_OPERATOR(); COMPANY_LEGAL_REPRESENTATIVE = companyLegalRep; NOMINEE = nominee; PLATFORM_NEUMARK_SHARE = uint128(PLATFORM_TERMS.PLATFORM_NEUMARK_SHARE()); TOKEN_RATE_EXPIRES_AFTER = uint128(PLATFORM_TERMS.TOKEN_RATE_EXPIRES_AFTER()); NEUMARK = universe.neumark(); ETHER_TOKEN = universe.etherToken(); EURO_TOKEN = universe.euroToken(); ETHER_LOCK = LockedAccount(universe.etherLock()); EURO_LOCK = LockedAccount(universe.euroLock()); CURRENCY_RATES = ITokenExchangeRateOracle(universe.tokenExchangeRateOracle()); ETO_TERMS = etoTerms; EQUITY_TOKEN = equityToken; MIN_TICKET_EUR_ULPS = etoTerms.MIN_TICKET_EUR_ULPS(); MAX_AVAILABLE_TOKENS = etoTerms.MAX_AVAILABLE_TOKENS(); MAX_AVAILABLE_TOKENS_IN_WHITELIST = etoTerms.MAX_AVAILABLE_TOKENS_IN_WHITELIST(); MIN_NUMBER_OF_TOKENS = etoTerms.TOKEN_TERMS().MIN_NUMBER_OF_TOKENS(); MIN_TICKET_TOKENS = etoTerms.calculateTokenAmount(0, MIN_TICKET_EUR_ULPS); MAX_INVESTMENT_AMOUNT_EUR_ULPS = ETO_TERMS_CONSTRAINTS.MAX_INVESTMENT_AMOUNT_EUR_ULPS(); // set it to max(unit256) to reduce number of operations later if (MAX_INVESTMENT_AMOUNT_EUR_ULPS == 0) { MAX_INVESTMENT_AMOUNT_EUR_ULPS -= 1; } setupStateMachine( ETO_TERMS.DURATION_TERMS(), IETOCommitmentObserver(EQUITY_TOKEN.tokenController()) ); } //////////////////////// // External functions //////////////////////// /// @dev sets timed state machine in motion function setStartDate( ETOTerms etoTerms, IEquityToken equityToken, uint256 startDate ) external onlyCompany onlyWithAgreement withStateTransition() onlyState(ETOState.Setup) { require(etoTerms == ETO_TERMS); require(equityToken == EQUITY_TOKEN); assert(startDate < 0xFFFFFFFF); // must be more than NNN days (platform terms!) require( startDate > block.timestamp && startDate - block.timestamp > ETO_TERMS_CONSTRAINTS.DATE_TO_WHITELIST_MIN_DURATION(), "NF_ETO_DATE_TOO_EARLY"); // prevent re-setting start date if ETO starts too soon uint256 startAt = startOfInternal(ETOState.Whitelist); // block.timestamp must be less than startAt, otherwise timed state transition is done require( startAt == 0 || (startAt - block.timestamp > ETO_TERMS_CONSTRAINTS.DATE_TO_WHITELIST_MIN_DURATION()), "NF_ETO_START_TOO_SOON"); runStateMachine(uint32(startDate)); // todo: lock ETO_TERMS whitelist to be more trustless emit LogTermsSet(msg.sender, address(etoTerms), address(equityToken)); emit LogETOStartDateSet(msg.sender, startAt, startDate); } function companySignsInvestmentAgreement(string signedInvestmentAgreementUrl) public withStateTransition() onlyState(ETOState.Signing) onlyCompany { _signedInvestmentAgreementUrl = signedInvestmentAgreementUrl; emit LogCompanySignedAgreement(msg.sender, NOMINEE, signedInvestmentAgreementUrl); } function nomineeConfirmsInvestmentAgreement(string signedInvestmentAgreementUrl) public withStateTransition() onlyState(ETOState.Signing) onlyNominee { bytes32 nomineeHash = keccak256(abi.encodePacked(signedInvestmentAgreementUrl)); require(keccak256(abi.encodePacked(_signedInvestmentAgreementUrl)) == nomineeHash, "NF_INV_HASH"); // setting this variable will induce state transition to Claim via mAdavanceLogicState _nomineeSignedInvestmentAgreementUrlHash = nomineeHash; emit LogNomineeConfirmedAgreement(msg.sender, COMPANY_LEGAL_REPRESENTATIVE, signedInvestmentAgreementUrl); } // // Implements ICommitment // /// commit function happens via ERC223 callback that must happen from trusted payment token /// @dev data in case of LockedAccount contains investor address and investor is LockedAccount address function tokenFallback(address wallet, uint256 amount, bytes data) public withStateTransition() onlyStates(ETOState.Whitelist, ETOState.Public) { // we trust only tokens below require(msg.sender == address(ETHER_TOKEN) || msg.sender == address(EURO_TOKEN), "NF_ETO_UNK_TOKEN"); address investor = wallet; // if investing via locked account, set real investor address if (wallet == address(ETHER_LOCK) || wallet == address(EURO_LOCK)) { // data contains investor address investor = decodeAddress(data); } // compute euro equivalent of ETH investment uint256 equivEurUlps = msg.sender == address(EURO_TOKEN) ? amount : convertToEurEquiv(amount); // agreement accepted by act of reserving funds in this function acceptAgreementInternal(investor); // check eligibilty, terms and issue NEU + ET (,,,, uint256 equityTokenInt, uint256 fixedSlotEquityTokenInt, uint256 investorNmk) = issueTokens(investor, wallet, equivEurUlps); // update investor ticket bool firstTimeInvestment = updateInvestorTicket( investor, wallet, equivEurUlps, investorNmk, equityTokenInt, amount ); // update investment state updateTotalInvestment( equityTokenInt, fixedSlotEquityTokenInt, equivEurUlps, amount, firstTimeInvestment ); // log successful commitment emit LogFundsCommitted( investor, wallet, msg.sender, amount, equivEurUlps, equityTokenInt, EQUITY_TOKEN, investorNmk ); } // // Implements IETOCommitment // function claim() external withStateTransition() onlyStates(ETOState.Claim, ETOState.Payout) { claimTokensPrivate(msg.sender); } function refund() external withStateTransition() onlyState(ETOState.Refund) { refundTokensPrivate(msg.sender); } function payout() external withStateTransition() onlyState(ETOState.Payout) { // does nothing - all hapens in state transition } // // Getters // // // IETOCommitment getters // function signedInvestmentAgreementUrl() public constant returns (string) { return _signedInvestmentAgreementUrl; } function contributionSummary() public constant returns ( uint256 newShares, uint256 capitalIncreaseUlps, uint256 additionalContributionEth, uint256 additionalContributionEurUlps, uint256 tokenParticipationFeeInt, uint256 platformFeeEth, uint256 platformFeeEurUlps, uint256 sharePriceEurUlps ) { return ( _newShares, _newShares * ETO_TERMS.TOKEN_TERMS().SHARE_NOMINAL_VALUE_ULPS(), _additionalContributionEth, _additionalContributionEurUlps, _tokenParticipationFeeInt, _platformFeeEth, _platformFeeEurUlps, _newShares == 0 ? 0 : divRound(_totalEquivEurUlps, _newShares) ); } function etoTerms() public constant returns (ETOTerms) { return ETO_TERMS; } function equityToken() public constant returns (IEquityToken) { return EQUITY_TOKEN; } function nominee() public constant returns (address) { return NOMINEE; } function companyLegalRep() public constant returns (address) { return COMPANY_LEGAL_REPRESENTATIVE; } function singletons() public constant returns ( address tokenOfferingOperator, address universe, address platformTerms ) { tokenOfferingOperator = TOKEN_OFFERING_OPERATOR; universe = UNIVERSE; platformTerms = PLATFORM_TERMS; } function totalInvestment() public constant returns ( uint256 totalEquivEurUlps, uint256 totalTokensInt, uint256 totalInvestors ) { return (_totalEquivEurUlps, _totalTokensInt, _totalInvestors); } function calculateContribution(address investor, bool fromIcbmWallet, uint256 newInvestorContributionEurUlps) external constant // use timed state so we show what should be withStateTransition() returns ( bool isWhitelisted, bool isEligible, uint256 minTicketEurUlps, uint256 maxTicketEurUlps, uint256 equityTokenInt, uint256 neuRewardUlps, bool maxCapExceeded, uint256 spentAmountEurUlps ) { InvestmentTicket storage ticket = _tickets[investor]; uint256 fixedSlotsEquityTokenInt; ( isWhitelisted, isEligible, minTicketEurUlps, maxTicketEurUlps, equityTokenInt, fixedSlotsEquityTokenInt ) = ETO_TERMS.calculateContribution( investor, _totalEquivEurUlps, ticket.equivEurUlps, newInvestorContributionEurUlps, // we use state() here because time was forwarded by withStateTransition state() == ETOState.Whitelist ); isWhitelisted = isWhitelisted || fromIcbmWallet; if (!fromIcbmWallet) { (,neuRewardUlps) = calculateNeumarkDistribution(NEUMARK.incremental(newInvestorContributionEurUlps)); } // crossing max cap can always happen maxCapExceeded = isCapExceeded(state() == ETOState.Whitelist, equityTokenInt, fixedSlotsEquityTokenInt, newInvestorContributionEurUlps); // todo: take it from ETO_TERMS.calculateContribution spentAmountEurUlps = newInvestorContributionEurUlps; } function investorTicket(address investor) public constant returns ( uint256 equivEurUlps, uint256 rewardNmkUlps, uint256 equityTokenInt, uint256 sharesFrac, uint256 tokenPrice, uint256 neuRate, uint256 amountEth, uint256 amountEurUlps, bool claimedOrRefunded, bool usedLockedAccount ) { InvestmentTicket storage ticket = _tickets[investor]; // here we assume that equity token precisions is 0 equivEurUlps = ticket.equivEurUlps; rewardNmkUlps = ticket.rewardNmkUlps; equityTokenInt = ticket.equityTokenInt; sharesFrac = ETO_TERMS.equityTokensToShares(ticket.equityTokenInt); tokenPrice = equityTokenInt > 0 ? equivEurUlps / equityTokenInt : 0; neuRate = rewardNmkUlps > 0 ? proportion(equivEurUlps, 10**18, rewardNmkUlps) : 0; amountEth = ticket.amountEth; amountEurUlps = ticket.amountEurUlps; claimedOrRefunded = ticket.claimOrRefundSettled; usedLockedAccount = ticket.usedLockedAccount; } // recycle all payment tokens held on this contract as a result of NEU proceeds // warning: it may also be used to recycle NEU and EQUITY TOKENS if any of those // will be disbursed to NEU holders. we couldn't find any problems coming from it // it's good to remember it though function recycle(address[] tokens) public onlyState(ETOState.Payout) { IFeeDisbursal disbursal = IFeeDisbursal(UNIVERSE.feeDisbursal()); for (uint256 i = 0; i < tokens.length; i += 1) { address token = tokens[i]; disbursal.reject(token, NEUMARK, 256**2-1); } } // // Implements IContractId // function contractId() public pure returns (bytes32 id, uint256 version) { return (0x70ef68fc8c585f9edc7af1bfac26c4b1b9e98ba05cf5ddd99e4b3dc46ea70073, 2); } //////////////////////// // Internal functions //////////////////////// // // Overrides internal interface // function mAdavanceLogicState(ETOState oldState) internal constant returns (ETOState) { // add 1 to MIN_TICKET_TOKEN because it was produced by floor and check only MAX CAP // WHITELIST CAP will not induce state transition as fixed slots should be able to invest till the end of Whitelist // also put the minimum ticket size plus one cent as eur equivalent to see wether we would cross the threshold bool capExceeded = isCapExceeded(false, MIN_TICKET_TOKENS + 1, 0, MIN_TICKET_EUR_ULPS); if (capExceeded) { if (oldState == ETOState.Whitelist) { return ETOState.Public; } if (oldState == ETOState.Public) { return ETOState.Signing; } } if (oldState == ETOState.Signing && _nomineeSignedInvestmentAgreementUrlHash != bytes32(0)) { return ETOState.Claim; } return oldState; } function mBeforeStateTransition(ETOState /*oldState*/, ETOState newState) internal constant returns (ETOState) { // force refund if floor criteria are not met // todo: allow for super edge case when MIN_NUMBER_OF_TOKENS is very close to MAX_AVAILABLE_TOKENS and we are within minimum ticket if (newState == ETOState.Signing && _totalTokensInt < MIN_NUMBER_OF_TOKENS) { return ETOState.Refund; } // go to refund if attempt to go to Claim without nominee agreement confirmation // if (newState == ETOState.Claim && _nomineeSignedInvestmentAgreementUrlHash == bytes32(0)) { // return ETOState.Refund; // } return newState; } function mAfterTransition(ETOState /*oldState*/, ETOState newState) internal { if (newState == ETOState.Signing) { onSigningTransition(); } if (newState == ETOState.Claim) { onClaimTransition(); } if (newState == ETOState.Refund) { onRefundTransition(); } if (newState == ETOState.Payout) { onPayoutTransition(); } } // // Overrides Agreement internal interface // function mCanAmend(address legalRepresentative) internal returns (bool) { return legalRepresentative == NOMINEE && startOfInternal(ETOState.Whitelist) == 0; } //////////////////////// // Private functions //////////////////////// // a copy of PlatformTerms working on local storage function calculateNeumarkDistribution(uint256 rewardNmk) private constant 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); } /// called on transition to Signing function onSigningTransition() private { ETOTokenTerms tokenTerms = ETO_TERMS.TOKEN_TERMS(); // additional equity tokens are issued and sent to platform operator (temporarily) uint256 tokensPerShare = tokenTerms.EQUITY_TOKENS_PER_SHARE(); // calculatePlatformTokenFee and calculateAmountWithoutFee are not exactly reversible so // there's a rounding discrepancy with period of 51 when those methods are called. see PlatformTerm.js for a test // so if we sold MAX_AVAILABLE_TOKENS that were computed via calculateAmountWithoutFee from MAXIMUM_NUMBER_OF_TOKENS // the reverse operation, that is MAX_AVAILABLE_TOKENS + calculatePlatformTokenFee will not always be MAXIMUM_NUMBER_OF_TOKENS // while it's probably possible to detect it we put this check here uint256 tokenParticipationFeeInt; if (_totalTokensInt == MAX_AVAILABLE_TOKENS) { uint256 maxTokens = tokenTerms.MAX_NUMBER_OF_TOKENS(); // rest up until MAX NUMBER OF TOKENS is our fee // we also assume that MAX_NUMBER_OF_TOKENS amount to full shares, which token terms contract checks tokenParticipationFeeInt = maxTokens - MAX_AVAILABLE_TOKENS; } else { tokenParticipationFeeInt = PLATFORM_TERMS.calculatePlatformTokenFee(_totalTokensInt); // we must have integer number of shares uint256 tokensRemainder = (_totalTokensInt + tokenParticipationFeeInt) % tokensPerShare; if (tokensRemainder > 0) { // round up to whole share tokenParticipationFeeInt += tokensPerShare - tokensRemainder; } } // will not cross max number of tokens thats in 2**56 range uint256 totalIssuedTokens = _totalTokensInt + tokenParticipationFeeInt; // round number of shares // require(totalIssuedTokens % tokensPerShare == 0, "NF_MUST_ISSUE_WHOLE_SHARES"); // we could not cross maximum number of tokens // require(totalIssuedTokens <= maxTokens, "NF_FEE_CROSSING_CAP"); // todo: optimizer will not handle code below, correction needs to separate storage reads and writes _newShares = uint64(totalIssuedTokens / tokensPerShare); // preserve platform token participation fee to be send out on claim transition _tokenParticipationFeeInt = uint64(tokenParticipationFeeInt); // compute fees to be sent on payout transition // 112bit values 2**112 / 10**18 which is 5 quadrillion 192 trillion 296 billion 858 million 534 thousand 827 nEUR max // also we capped balances already when taking from tokens _platformFeeEth = uint112(PLATFORM_TERMS.calculatePlatformFee(_additionalContributionEth)); _platformFeeEurUlps = uint112(PLATFORM_TERMS.calculatePlatformFee(_additionalContributionEurUlps)); // compute additional contributions to be sent on claim transition _additionalContributionEth -= _platformFeeEth; _additionalContributionEurUlps -= _platformFeeEurUlps; // nominee gets nominal share value immediately to be added to cap table uint256 capitalIncreaseEurUlps = tokenTerms.SHARE_NOMINAL_VALUE_EUR_ULPS() * _newShares; // limit the amount if balance on EURO_TOKEN < capitalIncreaseEurUlps. in that case Nomine must handle it offchain // no overflow as smaller one is uint112 uint112 availableCapitalEurUlps = uint112(min(capitalIncreaseEurUlps, _additionalContributionEurUlps)); assert(EURO_TOKEN.transfer(NOMINEE, availableCapitalEurUlps, "")); // decrease additional contribution by value that was sent to nominee _additionalContributionEurUlps -= availableCapitalEurUlps; // capital increase in ISHA currency, and report it uint256 capitalIncreaseUlps = tokenTerms.SHARE_NOMINAL_VALUE_ULPS() * _newShares; emit LogSigningStarted(NOMINEE, COMPANY_LEGAL_REPRESENTATIVE, _newShares, capitalIncreaseUlps); } /// called on transition to ETOState.Claim function onClaimTransition() private { // platform operator gets share of NEU uint256 rewardNmk = NEUMARK.balanceOf(this); (uint256 platformNmk,) = calculateNeumarkDistribution(rewardNmk); // will transfer operator share of NEU generated in this ETO (and from erroneous/malicious transfers) assert(NEUMARK.transfer(TOKEN_OFFERING_OPERATOR, platformNmk, "")); // company legal rep receives funds if (_additionalContributionEth > 0) { assert(ETHER_TOKEN.transfer(COMPANY_LEGAL_REPRESENTATIVE, _additionalContributionEth, "")); } if (_additionalContributionEurUlps > 0) { assert(EURO_TOKEN.transfer(COMPANY_LEGAL_REPRESENTATIVE, _additionalContributionEurUlps, "")); } // issue missing tokens EQUITY_TOKEN.issueTokens(_tokenParticipationFeeInt); emit LogPlatformNeuReward(TOKEN_OFFERING_OPERATOR, rewardNmk, platformNmk); emit LogAdditionalContribution(COMPANY_LEGAL_REPRESENTATIVE, ETHER_TOKEN, _additionalContributionEth); emit LogAdditionalContribution(COMPANY_LEGAL_REPRESENTATIVE, EURO_TOKEN, _additionalContributionEurUlps); } /// called on transtion to ETOState.Refund function onRefundTransition() private { // burn all neumark generated in this ETO (will also burn NEU sent via erroneous/malicious transfers) uint256 balanceNmk = NEUMARK.balanceOf(this); uint256 balanceTokenInt = EQUITY_TOKEN.balanceOf(this); if (balanceNmk > 0) { NEUMARK.burn(balanceNmk); } // destroy all tokens generated in ETO if (balanceTokenInt > 0) { EQUITY_TOKEN.destroyTokens(balanceTokenInt); } emit LogRefundStarted(EQUITY_TOKEN, balanceTokenInt, balanceNmk); } /// called on transition to ETOState.Payout function onPayoutTransition() private { // distribute what's left in balances: company took funds on claim IFeeDisbursal disbursal = IFeeDisbursal(UNIVERSE.feeDisbursal()); assert(disbursal != address(0)); address platformPortfolio = UNIVERSE.platformPortfolio(); assert(platformPortfolio != address(0)); bytes memory serializedAddress = abi.encodePacked(address(NEUMARK)); if (_platformFeeEth > 0) { // disburse via ERC223, where we encode token used to provide pro-rata in `data` parameter assert(ETHER_TOKEN.transfer(disbursal, _platformFeeEth, serializedAddress)); } if (_platformFeeEurUlps > 0) { // disburse via ERC223 assert(EURO_TOKEN.transfer(disbursal, _platformFeeEurUlps, serializedAddress)); } // if any payouts are pending for this contract, recycle them, there are two reasons to get pending payouts // 1. not all people claimed // 2. during the ETO contract received some payouts from other ETOs that finished // we should leave it to some periodic watched which would reject any substantial amounts // disbursal.reject(EURO_TOKEN, NEUMARK, 256**2-1); // disbursal.reject(ETHER_TOKEN, NEUMARK, 256**2-1); // add token participation fee to platfrom portfolio EQUITY_TOKEN.distributeTokens(platformPortfolio, _tokenParticipationFeeInt); emit LogPlatformFeePayout(ETHER_TOKEN, disbursal, _platformFeeEth); emit LogPlatformFeePayout(EURO_TOKEN, disbursal, _platformFeeEurUlps); emit LogPlatformPortfolioPayout(EQUITY_TOKEN, platformPortfolio, _tokenParticipationFeeInt); } function issueTokens( address investor, address wallet, uint256 equivEurUlps ) private returns ( bool isWhitelisted, bool isEligible, uint256 minTicketEurUlps, uint256 maxTicketEurUlps, uint256 equityTokenInt, uint256 fixedSlotEquityTokenInt, uint256 investorNmk ) { // read current ticket InvestmentTicket storage ticket = _tickets[investor]; // should we apply whitelist discounts bool applyDiscounts = state() == ETOState.Whitelist; // calculate contribution ( isWhitelisted, isEligible, minTicketEurUlps, maxTicketEurUlps, equityTokenInt, fixedSlotEquityTokenInt ) = ETO_TERMS.calculateContribution(investor, _totalEquivEurUlps, ticket.equivEurUlps, equivEurUlps, applyDiscounts); // kick out on KYC require(isEligible, "NF_ETO_INV_NOT_ELIGIBLE"); // kick on minimum ticket and you must buy at least one token! require(equivEurUlps + ticket.equivEurUlps >= minTicketEurUlps && equityTokenInt > 0, "NF_ETO_MIN_TICKET"); // kick on max ticket exceeded require(equivEurUlps + ticket.equivEurUlps <= maxTicketEurUlps, "NF_ETO_MAX_TICKET"); // kick on cap exceeded require(!isCapExceeded(applyDiscounts, equityTokenInt, fixedSlotEquityTokenInt, equivEurUlps), "NF_ETO_MAX_TOK_CAP"); // when that sent money is not the same as investor it must be icbm locked wallet // bool isLockedAccount = wallet != investor; // kick out not whitelist or not LockedAccount if (applyDiscounts) { require(isWhitelisted || wallet != investor, "NF_ETO_NOT_ON_WL"); } // we trust NEU token so we issue NEU before writing state // issue only for "new money" so LockedAccount from ICBM is excluded if (wallet == investor) { (, investorNmk) = calculateNeumarkDistribution(NEUMARK.issueForEuro(equivEurUlps)); if (investorNmk > 0) { // now there is rounding danger as we calculate the above for any investor but then just once to get platform share in onClaimTransition // it is much cheaper to just round down than to book keep to a single wei which will use additional storage // small amount of NEU ( no of investors * (PLATFORM_NEUMARK_SHARE-1)) may be left in contract assert(investorNmk > PLATFORM_NEUMARK_SHARE - 1); investorNmk -= PLATFORM_NEUMARK_SHARE - 1; } } // issue equity token EQUITY_TOKEN.issueTokens(equityTokenInt); } function convertToEurEquiv(uint256 amountEth) private constant returns (uint256) { // compute EUR eurEquivalent via oracle if ether (uint256 rate, uint256 rateTimestamp) = CURRENCY_RATES.getExchangeRate(ETHER_TOKEN, EURO_TOKEN); //require if rate older than 4 hours require(block.timestamp - rateTimestamp < TOKEN_RATE_EXPIRES_AFTER, "NF_ETO_INVALID_ETH_RATE"); return decimalFraction(amountEth, rate); } function updateInvestorTicket( address investor, address wallet, uint256 equivEurUlps, uint256 investorNmk, uint256 equityTokenInt, uint256 amount ) private returns (bool firstTimeInvestor) { InvestmentTicket storage ticket = _tickets[investor]; firstTimeInvestor = ticket.equivEurUlps == 0; // write new ticket values // this will also check ticket.amountEurUlps + uint96(amount) as ticket.equivEurUlps is always >= ticket.amountEurUlps uint256 newEquivEurUlps = ticket.equivEurUlps + equivEurUlps; require(newEquivEurUlps < 2**96, "NF_TICKET_EXCEEDS_MAX_EUR"); // uint96 is much more than 1.5 bln of NEU so no overflow uint96 newRewardNmkUlps = ticket.rewardNmkUlps + uint96(investorNmk); // if total number of tokens fits into uint56, particular investor tokens will fit into 96 uint96 newEquityTokenInt = ticket.equityTokenInt + uint96(equityTokenInt); // practically impossible: would require price of ETH smaller than 1 EUR and > 2**96 amount of ether // assert(ticket.amountEth + amount < 2**96); uint96 newAmountEth = ticket.amountEth; uint96 newAmountEurUlps = ticket.amountEurUlps; if (msg.sender == address(EURO_TOKEN)) { newAmountEurUlps += uint96(amount); } else { newAmountEth += uint96(amount); } // mark if locked account was used at least once by investor bool usedLockedAccount = ticket.usedLockedAccount || investor != wallet; // write in single pack, hopefully storage will be optimized... ticket.equivEurUlps = uint96(newEquivEurUlps); ticket.rewardNmkUlps = newRewardNmkUlps; ticket.equityTokenInt = newEquityTokenInt; ticket.amountEth = newAmountEth; ticket.amountEurUlps = newAmountEurUlps; ticket.usedLockedAccount = usedLockedAccount; } function updateTotalInvestment( uint256 equityTokenInt, uint256 fixedSlotEquityTokenInt, uint256 equivEurUlps, uint256 amount, bool firstTimeInvestment ) private { // total number of tokens must fit into uint56, uints are coerced to biggest size uint256 newTotalTokensInt = _totalTokensInt + equityTokenInt; assert(newTotalTokensInt < 2**56); // as fixedSlotEquityTokenInt256 always lte equityTokenInt no chances to overflow uint56 newTotalFixedSlotsTokensInt = _totalFixedSlotsTokensInt + uint56(fixedSlotEquityTokenInt); // add new investor uint32 newTotalInvestors = _totalInvestors + (firstTimeInvestment ? 1 : 0); // new total eur equivalent invested uint256 newTotalEquivEurUlps = _totalEquivEurUlps + equivEurUlps; assert(newTotalEquivEurUlps < 2**112); // resue eth/neur contribution slots for native currency bookkeeping uint112 newTotalEth = _additionalContributionEth; uint112 newTotalEurUlps = _additionalContributionEurUlps; if (msg.sender == address(EURO_TOKEN)) { newTotalEurUlps += uint112(amount); } else { newTotalEth += uint112(amount); } // write to storage in one go so maybe optimizer will work _totalTokensInt = uint56(newTotalTokensInt); _totalFixedSlotsTokensInt = newTotalFixedSlotsTokensInt; _totalInvestors = newTotalInvestors; _totalEquivEurUlps = uint112(newTotalEquivEurUlps); _additionalContributionEth = newTotalEth; _additionalContributionEurUlps = newTotalEurUlps; } function isCapExceeded(bool applyDiscounts, uint256 equityTokenInt, uint256 fixedSlotsEquityTokenInt, uint256 equivEurUlps) private constant returns (bool maxCapExceeded) { // check for exceeding tokens maxCapExceeded = _totalTokensInt + equityTokenInt > MAX_AVAILABLE_TOKENS; if (applyDiscounts && !maxCapExceeded) { maxCapExceeded = _totalTokensInt + equityTokenInt - _totalFixedSlotsTokensInt - fixedSlotsEquityTokenInt > MAX_AVAILABLE_TOKENS_IN_WHITELIST; } // check for exceeding max investment amount as defined by the constraints, MAX_INVESTMENT_AMOUNT_EUR_ULPS is always > 0 if ( !maxCapExceeded && (equivEurUlps + _totalEquivEurUlps > MAX_INVESTMENT_AMOUNT_EUR_ULPS )) { maxCapExceeded = true; } } function claimTokensPrivate(address investor) private { InvestmentTicket storage ticket = _tickets[investor]; if (ticket.claimOrRefundSettled) { return; } if (ticket.equivEurUlps == 0) { return; } ticket.claimOrRefundSettled = true; if (ticket.rewardNmkUlps > 0) { NEUMARK.distribute(investor, ticket.rewardNmkUlps); } if (ticket.equityTokenInt > 0) { EQUITY_TOKEN.distributeTokens(investor, ticket.equityTokenInt); } if (ticket.usedLockedAccount) { ETHER_LOCK.claimed(investor); EURO_LOCK.claimed(investor); } emit LogTokensClaimed(investor, EQUITY_TOKEN, ticket.equityTokenInt, ticket.rewardNmkUlps); } function refundTokensPrivate(address investor) private { InvestmentTicket storage ticket = _tickets[investor]; if (ticket.claimOrRefundSettled) { return; } if (ticket.equivEurUlps == 0) { return; } ticket.claimOrRefundSettled = true; refundSingleToken(investor, ticket.amountEth, ticket.usedLockedAccount, ETHER_LOCK, ETHER_TOKEN); refundSingleToken(investor, ticket.amountEurUlps, ticket.usedLockedAccount, EURO_LOCK, EURO_TOKEN); emit LogFundsRefunded(investor, ETHER_TOKEN, ticket.amountEth); emit LogFundsRefunded(investor, EURO_TOKEN, ticket.amountEurUlps); } function refundSingleToken( address investor, uint256 amount, bool usedLockedAccount, LockedAccount lockedAccount, IERC223Token token ) private { if (amount == 0) { return; } uint256 a = amount; // possible partial refund to locked account if (usedLockedAccount) { (uint256 balance,) = lockedAccount.pendingCommitments(this, investor); assert(balance <= a); if (balance > 0) { assert(token.approve(address(lockedAccount), balance)); lockedAccount.refunded(investor); a -= balance; } } if (a > 0) { assert(token.transfer(investor, a, "")); } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"constant":true,"inputs":[],"name":"timedState","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"success","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"signedInvestmentAgreementUrl","type":"string"}],"name":"companySignsInvestmentAgreement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalInvestment","outputs":[{"name":"totalEquivEurUlps","type":"uint256"},{"name":"totalTokensInt","type":"uint256"},{"name":"totalInvestors","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"startOfStates","outputs":[{"name":"startOfs","type":"uint256[7]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nominee","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"etoTerms","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"commitmentObserver","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"TOKEN_OFFERING_OPERATOR","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributionSummary","outputs":[{"name":"newShares","type":"uint256"},{"name":"capitalIncreaseUlps","type":"uint256"},{"name":"additionalContributionEth","type":"uint256"},{"name":"additionalContributionEurUlps","type":"uint256"},{"name":"tokenParticipationFeeInt","type":"uint256"},{"name":"platformFeeEth","type":"uint256"},{"name":"platformFeeEurUlps","type":"uint256"},{"name":"sharePriceEurUlps","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claim","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newPolicy","type":"address"},{"name":"newAccessController","type":"address"}],"name":"setAccessPolicy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ETO_TERMS_CONSTRAINTS","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"etoTerms","type":"address"},{"name":"equityToken","type":"address"},{"name":"startDate","type":"uint256"}],"name":"setStartDate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"payout","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"signedInvestmentAgreementUrl","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"signatory","type":"address"}],"name":"agreementSignedAtBlock","outputs":[{"name":"blockNo","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"amendmentsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contractId","outputs":[{"name":"id","type":"bytes32"},{"name":"version","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"singletons","outputs":[{"name":"tokenOfferingOperator","type":"address"},{"name":"universe","type":"address"},{"name":"platformTerms","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokens","type":"address[]"}],"name":"recycle","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"amendmentIndex","type":"uint256"}],"name":"pastAgreement","outputs":[{"name":"contractLegalRepresentative","type":"address"},{"name":"signedBlockTimestamp","type":"uint256"},{"name":"agreementUri","type":"string"},{"name":"index","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"s","type":"uint8"}],"name":"startOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"finalized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"investor","type":"address"}],"name":"investorTicket","outputs":[{"name":"equivEurUlps","type":"uint256"},{"name":"rewardNmkUlps","type":"uint256"},{"name":"equityTokenInt","type":"uint256"},{"name":"sharesFrac","type":"uint256"},{"name":"tokenPrice","type":"uint256"},{"name":"neuRate","type":"uint256"},{"name":"amountEth","type":"uint256"},{"name":"amountEurUlps","type":"uint256"},{"name":"claimedOrRefunded","type":"bool"},{"name":"usedLockedAccount","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"failed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"wallet","type":"address"},{"name":"amount","type":"uint256"},{"name":"data","type":"bytes"}],"name":"tokenFallback","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"equityToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentAgreement","outputs":[{"name":"contractLegalRepresentative","type":"address"},{"name":"signedBlockTimestamp","type":"uint256"},{"name":"agreementUri","type":"string"},{"name":"index","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"investor","type":"address"},{"name":"fromIcbmWallet","type":"bool"},{"name":"newInvestorContributionEurUlps","type":"uint256"}],"name":"calculateContribution","outputs":[{"name":"isWhitelisted","type":"bool"},{"name":"isEligible","type":"bool"},{"name":"minTicketEurUlps","type":"uint256"},{"name":"maxTicketEurUlps","type":"uint256"},{"name":"equityTokenInt","type":"uint256"},{"name":"neuRewardUlps","type":"uint256"},{"name":"maxCapExceeded","type":"bool"},{"name":"spentAmountEurUlps","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ethereumForkArbiter","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"agreementUri","type":"string"}],"name":"amendAgreement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"accessPolicy","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"handleStateTransitions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"signedInvestmentAgreementUrl","type":"string"}],"name":"nomineeConfirmsInvestmentAgreement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"companyLegalRep","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"universe","type":"address"},{"name":"nominee","type":"address"},{"name":"companyLegalRep","type":"address"},{"name":"etoTerms","type":"address"},{"name":"equityToken","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"companyLegalRep","type":"address"},{"indexed":false,"name":"paymentToken","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"LogAdditionalContribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokenOfferingOperator","type":"address"},{"indexed":false,"name":"totalRewardNmkUlps","type":"uint256"},{"indexed":false,"name":"platformRewardNmkUlps","type":"uint256"}],"name":"LogPlatformNeuReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"paymentToken","type":"address"},{"indexed":false,"name":"disbursalPool","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"LogPlatformFeePayout","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"assetToken","type":"address"},{"indexed":false,"name":"platformPortfolio","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"LogPlatformPortfolioPayout","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldState","type":"uint32"},{"indexed":false,"name":"newState","type":"uint32"},{"indexed":false,"name":"timestamp","type":"uint32"}],"name":"LogStateTransition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":true,"name":"assetToken","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"nmkReward","type":"uint256"}],"name":"LogTokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":true,"name":"paymentToken","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"LogFundsRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"companyLegalRep","type":"address"},{"indexed":false,"name":"etoTerms","type":"address"},{"indexed":false,"name":"equityToken","type":"address"}],"name":"LogTermsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"companyLegalRep","type":"address"},{"indexed":false,"name":"previousTimestamp","type":"uint256"},{"indexed":false,"name":"newTimestamp","type":"uint256"}],"name":"LogETOStartDateSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nominee","type":"address"},{"indexed":false,"name":"companyLegalRep","type":"address"},{"indexed":false,"name":"newShares","type":"uint256"},{"indexed":false,"name":"capitalIncreaseUlps","type":"uint256"}],"name":"LogSigningStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"companyLegalRep","type":"address"},{"indexed":false,"name":"nominee","type":"address"},{"indexed":false,"name":"signedInvestmentAgreementUrl","type":"string"}],"name":"LogCompanySignedAgreement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nominee","type":"address"},{"indexed":false,"name":"companyLegalRep","type":"address"},{"indexed":false,"name":"signedInvestmentAgreementUrl","type":"string"}],"name":"LogNomineeConfirmedAgreement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"assetToken","type":"address"},{"indexed":false,"name":"totalTokenAmountInt","type":"uint256"},{"indexed":false,"name":"totalRewardNmkUlps","type":"uint256"}],"name":"LogRefundStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":false,"name":"wallet","type":"address"},{"indexed":false,"name":"paymentToken","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"baseCurrencyEquivalent","type":"uint256"},{"indexed":false,"name":"grantedAmount","type":"uint256"},{"indexed":false,"name":"assetToken","type":"address"},{"indexed":false,"name":"neuReward","type":"uint256"}],"name":"LogFundsCommitted","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"},{"anonymous":false,"inputs":[{"indexed":true,"name":"accepter","type":"address"}],"name":"LogAgreementAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"contractLegalRepresentative","type":"address"},{"indexed":false,"name":"agreementUri","type":"string"}],"name":"LogAgreementAmended","type":"event"}]
Contract Creation Code
60806040526005805460a060020a60ff02191690553480156200002157600080fd5b5060405160a0806200681c83398101604081815282516020808501518386015160608701516080909701517ff5d60a51000000000000000000000000000000000000000000000000000000008752945193969195909491939192600160a060020a0388169263f5d60a519260048082019392918290030181600087803b158015620000ab57600080fd5b505af1158015620000c0573d6000803e3d6000fd5b505050506040513d6020811015620000d757600080fd5b5051604080517f23dc2b570000000000000000000000000000000000000000000000000000000081529051600160a060020a038816916323dc2b579160048083019260209291908290030181600087803b1580156200013557600080fd5b505af11580156200014a573d6000803e3d6000fd5b505050506040513d60208110156200016157600080fd5b505181600160a060020a03811615156200017a57600080fd5b60008054600160a060020a031916600160a060020a0392831617905581161515620001a457600080fd5b60018054600160a060020a03928316600160a060020a031991821617909155600780549289169290911682179055604080517fc4916071000000000000000000000000000000000000000000000000000000008152905191925063c49160719160048083019260209291908290030181600087803b1580156200022657600080fd5b505af11580156200023b573d6000803e3d6000fd5b505050506040513d60208110156200025257600080fd5b5051601a8054600160a060020a031916600160a060020a03928316179055604080517fc0c7956700000000000000000000000000000000000000000000000000000000815290519184169163c0c79567916004808201926020929091908290030181600087803b158015620002c657600080fd5b505af1158015620002db573d6000803e3d6000fd5b505050506040513d6020811015620002f257600080fd5b5051604080517f66448ae80000000000000000000000000000000000000000000000000000000081529051600160a060020a03909216916366448ae8916004808201926020929091908290030181600087803b1580156200035257600080fd5b505af115801562000367573d6000803e3d6000fd5b505050506040513d60208110156200037e57600080fd5b5051604080517f313ce567000000000000000000000000000000000000000000000000000000008152905160ff90921691600160a060020a0384169163313ce5679160048083019260209291908290030181600087803b158015620003e257600080fd5b505af1158015620003f7573d6000803e3d6000fd5b505050506040513d60208110156200040e57600080fd5b505160ff16146200041e57600080fd5b600160a060020a038416158015906200043f5750600160a060020a03831615155b15156200044b57600080fd5b81600160a060020a0316635aa2b2066040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b158015620004a357600080fd5b505af1158015620004b8573d6000803e3d6000fd5b505050506040513d6020811015620004cf57600080fd5b505160198054600160a060020a031916600160a060020a039283161790819055604080517f45067cfb000000000000000000000000000000000000000000000000000000008152905191909216916345067cfb9160048083019260209291908290030181600087803b1580156200054557600080fd5b505af11580156200055a573d6000803e3d6000fd5b505050506040513d60208110156200057157600080fd5b5051601b8054600160a060020a0319908116600160a060020a039384161790915560168054821686841617905560178054909116868316179055601a54604080517f659c713b0000000000000000000000000000000000000000000000000000000081529051919092169163659c713b9160048281019260209291908290030181600087803b1580156200060457600080fd5b505af115801562000619573d6000803e3d6000fd5b505050506040513d60208110156200063057600080fd5b5051601380546001608060020a0319166001608060020a03909216919091179055601a54604080517fe1546a230000000000000000000000000000000000000000000000000000000081529051600160a060020a039092169163e1546a23916004808201926020929091908290030181600087803b158015620006b257600080fd5b505af1158015620006c7573d6000803e3d6000fd5b505050506040513d6020811015620006de57600080fd5b5051601380546001608060020a03928316700100000000000000000000000000000000029216919091179055604080517fe97b97fe0000000000000000000000000000000000000000000000000000000081529051600160a060020a0387169163e97b97fe9160048083019260209291908290030181600087803b1580156200076657600080fd5b505af11580156200077b573d6000803e3d6000fd5b505050506040513d60208110156200079257600080fd5b505160088054600160a060020a031916600160a060020a03928316179055604080517fb8066bcb00000000000000000000000000000000000000000000000000000000815290519187169163b8066bcb916004808201926020929091908290030181600087803b1580156200080657600080fd5b505af11580156200081b573d6000803e3d6000fd5b505050506040513d60208110156200083257600080fd5b505160098054600160a060020a031916600160a060020a03928316179055604080517f2dfb39fd000000000000000000000000000000000000000000000000000000008152905191871691632dfb39fd916004808201926020929091908290030181600087803b158015620008a657600080fd5b505af1158015620008bb573d6000803e3d6000fd5b505050506040513d6020811015620008d257600080fd5b5051600a8054600160a060020a031916600160a060020a03928316179055604080517fe0f20a6500000000000000000000000000000000000000000000000000000000815290519187169163e0f20a65916004808201926020929091908290030181600087803b1580156200094657600080fd5b505af11580156200095b573d6000803e3d6000fd5b505050506040513d60208110156200097257600080fd5b5051600b8054600160a060020a031916600160a060020a03928316179055604080517f870550080000000000000000000000000000000000000000000000000000000081529051918716916387055008916004808201926020929091908290030181600087803b158015620009e657600080fd5b505af1158015620009fb573d6000803e3d6000fd5b505050506040513d602081101562000a1257600080fd5b5051600c8054600160a060020a031916600160a060020a03928316179055604080517f04ac69c80000000000000000000000000000000000000000000000000000000081529051918716916304ac69c8916004808201926020929091908290030181600087803b15801562000a8657600080fd5b505af115801562000a9b573d6000803e3d6000fd5b505050506040513d602081101562000ab257600080fd5b5051600e8054600160a060020a0319908116600160a060020a0393841617909155601880548216858416908117909155600d8054909216928416929092179055604080517f548e73c7000000000000000000000000000000000000000000000000000000008152905163548e73c7916004808201926020929091908290030181600087803b15801562000b4457600080fd5b505af115801562000b59573d6000803e3d6000fd5b505050506040513d602081101562000b7057600080fd5b5051601555604080517f3352248d0000000000000000000000000000000000000000000000000000000081529051600160a060020a03841691633352248d9160048083019260209291908290030181600087803b15801562000bd157600080fd5b505af115801562000be6573d6000803e3d6000fd5b505050506040513d602081101562000bfd57600080fd5b5051601055604080517f8d22a0180000000000000000000000000000000000000000000000000000000081529051600160a060020a03841691638d22a0189160048083019260209291908290030181600087803b15801562000c5e57600080fd5b505af115801562000c73573d6000803e3d6000fd5b505050506040513d602081101562000c8a57600080fd5b5051601155604080517fc0c795670000000000000000000000000000000000000000000000000000000081529051600160a060020a0384169163c0c795679160048083019260209291908290030181600087803b15801562000ceb57600080fd5b505af115801562000d00573d6000803e3d6000fd5b505050506040513d602081101562000d1757600080fd5b5051604080517f3bdcb5ef0000000000000000000000000000000000000000000000000000000081529051600160a060020a0390921691633bdcb5ef916004808201926020929091908290030181600087803b15801562000d7757600080fd5b505af115801562000d8c573d6000803e3d6000fd5b505050506040513d602081101562000da357600080fd5b5051600f55601554604080517f0355383500000000000000000000000000000000000000000000000000000000815260006004820181905260248201939093529051600160a060020a03851692630355383592604480820193602093909283900390910190829087803b15801562000e1a57600080fd5b505af115801562000e2f573d6000803e3d6000fd5b505050506040513d602081101562000e4657600080fd5b5051601255601954604080517f379f99720000000000000000000000000000000000000000000000000000000081529051600160a060020a039092169163379f9972916004808201926020929091908290030181600087803b15801562000eac57600080fd5b505af115801562000ec1573d6000803e3d6000fd5b505050506040513d602081101562000ed857600080fd5b50516014819055151562000ef157601480546000190190555b601854604080517fc51ab7f600000000000000000000000000000000000000000000000000000000815290516200102092600160a060020a03169163c51ab7f69160048083019260209291908290030181600087803b15801562000f5457600080fd5b505af115801562000f69573d6000803e3d6000fd5b505050506040513d602081101562000f8057600080fd5b5051600d54604080517feddd9d820000000000000000000000000000000000000000000000000000000081529051600160a060020a039092169163eddd9d82916004808201926020929091908290030181600087803b15801562000fe357600080fd5b505af115801562000ff8573d6000803e3d6000fd5b505050506040513d60208110156200100f57600080fd5b50516401000000006200102b810204565b505050505062001445565b600554600160a060020a031615620010a457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f4e465f53544d5f5345545f4f4e43450000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0381161515620010ba57600080fd5b80600560006101000a815481600160a060020a030219169083600160a060020a0316021790555060e060405190810160405280600063ffffffff16815260200183600160a060020a031663571b1f8a6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1580156200115257600080fd5b505af115801562001167573d6000803e3d6000fd5b505050506040513d60208110156200117e57600080fd5b505163ffffffff168152604080517f2ca749420000000000000000000000000000000000000000000000000000000081529051602092830192600160a060020a03871692632ca7494292600480830193928290030181600087803b158015620011e657600080fd5b505af1158015620011fb573d6000803e3d6000fd5b505050506040513d60208110156200121257600080fd5b505163ffffffff168152604080517f7a95de210000000000000000000000000000000000000000000000000000000081529051602092830192600160a060020a03871692637a95de2192600480830193928290030181600087803b1580156200127a57600080fd5b505af11580156200128f573d6000803e3d6000fd5b505050506040513d6020811015620012a657600080fd5b505163ffffffff168152604080517fff844a630000000000000000000000000000000000000000000000000000000081529051602092830192600160a060020a0387169263ff844a6392600480830193928290030181600087803b1580156200130e57600080fd5b505af115801562001323573d6000803e3d6000fd5b505050506040513d60208110156200133a57600080fd5b505163ffffffff1681526000602082018190526040909101526200136390600490600762001368565b505050565b828054828255906000526020600020906007016008900481019282156200140c5791602002820160005b83821115620013d857835183826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030262001392565b80156200140a5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302620013d8565b505b506200141a9291506200141e565b5090565b6200144291905b808211156200141a57805463ffffffff1916815560010162001425565b90565b6153c780620014556000396000f3006080604052600436106101be5763ffffffff60e060020a60003504166304f36f5981146101c35780630b93381b146101fc57806310716b251461022557806310ea13df14610280578063168250b6146102b357806320f99c0a14610300578063302fe516146103315780633d57f3ca1461034657806345067cfb1461035b578063468e4c73146103705780634e71d92d146103c657806357875631146103db578063590e1ae3146104025780635aa2b2061461041757806361c217811461042c57806363bd1d4a146104565780636b4158df1461046b5780636fa00f07146104f55780637b154313146105285780638291286c1461053d57806382dfe1501461056b57806388d73770146105ab5780638fb29d6c14610600578063a0f8be8c146106b7578063b3f05b97146106d2578063b568dfc0146106e7578063ba414fa61461075c578063c0ee0b8a14610771578063c19d93fb146107da578063c6b43514146107ef578063c90f793e14610804578063e7ee7ced14610819578063ea490b8414610887578063eb4e64d61461089c578063f5d60a51146108f5578063f71a78431461090a578063fa29b3e31461091f578063fdac7dfe14610978575b600080fd5b3480156101cf57600080fd5b506101d861098d565b604051808260068111156101e857fe5b60ff16815260200191505060405180910390f35b34801561020857600080fd5b506102116109a9565b604080519115158252519081900360200190f35b34801561023157600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261027e9436949293602493928401919081908401838280828437509497506109ee9650505050505050565b005b34801561028c57600080fd5b50610295610b10565b60408051938452602084019290925282820152519081900360600190f35b3480156102bf57600080fd5b506102c8610b3f565b604051808260e080838360005b838110156102ed5781810151838201526020016102d5565b5050505090500191505060405180910390f35b34801561030c57600080fd5b50610315610b84565b60408051600160a060020a039092168252519081900360200190f35b34801561033d57600080fd5b50610315610b93565b34801561035257600080fd5b50610315610ba2565b34801561036757600080fd5b50610315610bb1565b34801561037c57600080fd5b50610385610bc0565b604080519889526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b3480156103d257600080fd5b5061027e610da6565b3480156103e757600080fd5b5061027e600160a060020a0360043581169060243516610e18565b34801561040e57600080fd5b5061027e611075565b34801561042357600080fd5b506103156110b4565b34801561043857600080fd5b5061027e600160a060020a03600435811690602435166044356110c3565b34801561046257600080fd5b5061027e6113d2565b34801561047757600080fd5b506104806113ff565b6040805160208082528351818301528351919283929083019185019080838360005b838110156104ba5781810151838201526020016104a2565b50505050905090810190601f1680156104e75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561050157600080fd5b50610516600160a060020a0360043516611495565b60408051918252519081900360200190f35b34801561053457600080fd5b506105166114b0565b34801561054957600080fd5b506105526114b6565b6040805192835260208301919091528051918290030190f35b34801561057757600080fd5b506105806114dd565b60408051600160a060020a039485168152928416602084015292168183015290519081900360600190f35b3480156105b757600080fd5b506040805160206004803580820135838102808601850190965280855261027e953695939460249493850192918291850190849080828437509497506114fa9650505050505050565b34801561060c57600080fd5b5061061860043561166e565b6040518085600160a060020a0316600160a060020a0316815260200184815260200180602001838152602001828103825284818151815260200191508051906020019080838360005b83811015610679578181015183820152602001610661565b50505050905090810190601f1680156106a65780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b3480156106c357600080fd5b5061051660ff60043516611763565b3480156106de57600080fd5b50610211611774565b3480156106f357600080fd5b50610708600160a060020a03600435166117bf565b604080519a8b5260208b0199909952898901979097526060890195909552608088019390935260a087019190915260c086015260e08501521515610100840152151561012083015251908190036101400190f35b34801561076857600080fd5b50610211611926565b34801561077d57600080fd5b50604080516020600460443581810135601f810184900484028501840190955284845261027e948235600160a060020a031694602480359536959460649492019190819084018382808284375094975061192f9650505050505050565b3480156107e657600080fd5b506101d8611b2c565b3480156107fb57600080fd5b50610315611b3c565b34801561081057600080fd5b50610618611b4b565b34801561082557600080fd5b50610842600160a060020a03600435166024351515604435611c5b565b6040805198151589529615156020890152878701959095526060870193909352608086019190915260a0850152151560c084015260e083015251908190036101000190f35b34801561089357600080fd5b50610315611ec2565b3480156108a857600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261027e943694929360249392840191908190840183828082843750949750611ed19650505050505050565b34801561090157600080fd5b5061031561208d565b34801561091657600080fd5b5061027e61209c565b34801561092b57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261027e9436949293602493928401919081908401838280828437509497506120a49650505050505050565b34801561098457600080fd5b506103156123af565b60006109976123be565b5060055460a060020a900460ff165b90565b6000600460055460a060020a900460ff1660068111156109c557fe5b14806109e9575060055b60055460a060020a900460ff1660068111156109e757fe5b145b905090565b6109f66123be565b60038060055460a060020a900460ff166006811115610a1157fe5b14610a1b57600080fd5b601654600160a060020a03163314610a3257600080fd5b8151610a459060229060208501906152c9565b506017546040805133808252600160a060020a039093166020808301829052606093830184815287519484019490945286517f17d12ed04f5e56c750cbda26b99121d3068cfa151b827a8c4f9a0bcdaeb32cf495949293889390929091608084019185019080838360005b83811015610ac8578181015183820152602001610ab0565b50505050905090810190601f168015610af55780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a150610b0d612512565b50565b601d546001607060020a03811691607060020a820466ffffffffffffff169160e060020a900463ffffffff1690565b610b47615343565b60005b6007811015610b8057610b67816006811115610b6257fe5b612567565b828260078110610b7357fe5b6020020152600101610b4a565b5090565b601754600160a060020a031690565b601854600160a060020a031690565b600554600160a060020a031690565b601b54600160a060020a031681565b600080600080600080600080602060009054906101000a900467ffffffffffffffff16601860009054906101000a9004600160a060020a0316600160a060020a031663c0c795676040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015610c3657600080fd5b505af1158015610c4a573d6000803e3d6000fd5b505050506040513d6020811015610c6057600080fd5b5051604080517fc46ca0d80000000000000000000000000000000000000000000000000000000081529051600160a060020a039092169163c46ca0d8916004808201926020929091908290030181600087803b158015610cbf57600080fd5b505af1158015610cd3573d6000803e3d6000fd5b505050506040513d6020811015610ce957600080fd5b5051602054601f5460215467ffffffffffffffff808416948502946001607060020a0380851695607060020a90950481169468010000000000000000810490931693608060020a909304811692169015610d6557601d54602054610d60916001607060020a03169067ffffffffffffffff16612754565b610d68565b60005b67ffffffffffffffff9788169f50959d506001607060020a039485169c509284169a5094169750928116955091909116925090509091929394959697565b610dae6123be565b600460058160055460a060020a900460ff166006811115610dcb57fe5b1480610df85750806006811115610dde57fe5b60055460a060020a900460ff166006811115610df657fe5b145b1515610e0357600080fd5b610e0c33612775565b5050610e16612512565b565b60008054604080517f9085b77f0000000000000000000000000000000000000000000000000000000081523360048201527fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da602482018190523060448301527bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19853516606483015291519192600160a060020a031691639085b77f9160848082019260209290919082900301818887803b158015610ed257600080fd5b505af1158015610ee6573d6000803e3d6000fd5b505050506040513d6020811015610efc57600080fd5b50511515610f0957600080fd5b604080517f9085b77f000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301527fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da6024830152306044830152600080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166064840152925190871692639085b77f92608480820193602093909283900390910190829087803b158015610fc357600080fd5b505af1158015610fd7573d6000803e3d6000fd5b505050506040513d6020811015610fed57600080fd5b50511515610ffa57600080fd5b60008054600160a060020a0386811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040805133815291909216602082018190528183019390935290519193507f7d475c32583df95fccc34a6e12df24c1fc9943092cc129b6512013aecba0f136919081900360600190a150505050565b61107d6123be565b60068060055460a060020a900460ff16600681111561109857fe5b146110a257600080fd5b6110ab33612abe565b50610e16612512565b601954600160a060020a031681565b601654600090600160a060020a031633146110dd57600080fd5b60006110e76114b0565b116110f157600080fd5b6110f96123be565b60008060055460a060020a900460ff16600681111561111457fe5b1461111e57600080fd5b601854600160a060020a0386811691161461113857600080fd5b600d54600160a060020a0385811691161461115257600080fd5b63ffffffff831061115f57fe5b42831180156111ed5750601960009054906101000a9004600160a060020a0316600160a060020a031663cbf9e46a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156111bc57600080fd5b505af11580156111d0573d6000803e3d6000fd5b505050506040513d60208110156111e657600080fd5b5051428403115b1515611243576040805160e560020a62461bcd02815260206004820152601560248201527f4e465f45544f5f444154455f544f4f5f4541524c590000000000000000000000604482015290519081900360640190fd5b61124d6001612567565b91508115806112db5750601960009054906101000a9004600160a060020a0316600160a060020a031663cbf9e46a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156112aa57600080fd5b505af11580156112be573d6000803e3d6000fd5b505050506040513d60208110156112d457600080fd5b5051428303115b1515611331576040805160e560020a62461bcd02815260206004820152601560248201527f4e465f45544f5f53544152545f544f4f5f534f4f4e0000000000000000000000604482015290519081900360640190fd5b61133a83612c67565b60408051338152600160a060020a03808816602083015286168183015290517fd4a8a704f685c06e20e177cb50c64301b93b8590b5050406fd3095955620becb9181900360600190a1604080513381526020810184905280820185905290517fbbd191f3e264e3b631bed4fb848beb0126e0ec1ca54512ddf0a1cbdf966492869181900360600190a1506113cc612512565b50505050565b6113da6123be565b60058060055460a060020a900460ff1660068111156113f557fe5b146110ab57600080fd5b60228054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561148b5780601f106114605761010080835404028352916020019161148b565b820191906000526020600020905b81548152906001019060200180831161146e57829003601f168201915b5050505050905090565b600160a060020a031660009081526003602052604090205490565b60025490565b7f70ef68fc8c585f9edc7af1bfac26c4b1b9e98ba05cf5ddd99e4b3dc46ea7007360029091565b601b54600754601a54600160a060020a0392831693918316921690565b6000808060058060055460a060020a900460ff16600681111561151957fe5b1461152357600080fd5b600760009054906101000a9004600160a060020a0316600160a060020a031663494115576040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561157657600080fd5b505af115801561158a573d6000803e3d6000fd5b505050506040513d60208110156115a057600080fd5b50519350600092505b84518310156116675784838151811015156115c057fe5b60209081029091010151600854604080517f813bedf2000000000000000000000000000000000000000000000000000000008152600160a060020a038085166004830152928316602482015261ffff604482015290519294509086169163813bedf29160648082019260009290919082900301818387803b15801561164457600080fd5b505af1158015611658573d6000803e3d6000fd5b505050506001830192506115a9565b5050505050565b600080606060008060028681548110151561168557fe5b906000526020600020906003020190508060000160009054906101000a9004600160a060020a031681600101548260020188818054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561174c5780601f106117215761010080835404028352916020019161174c565b820191906000526020600020905b81548152906001019060200180831161172f57829003601f168201915b505050505091509450945094509450509193509193565b600061176e82612567565b92915050565b6000600660055460a060020a900460ff16600681111561179057fe5b14806117b257506005805460a060020a900460ff1660068111156117b057fe5b145b806109e9575060046109cf565b600160a060020a038082166000908152601c602090815260408083208054600182015460185484517fedf04bd40000000000000000000000000000000000000000000000000000000081526001606060020a0392831660048201819052955183851699606060020a909504909316979596869586958695869586958695919093169263edf04bd4926024808301939282900301818887803b15801561186357600080fd5b505af1158015611877573d6000803e3d6000fd5b505050506040513d602081101561188d57600080fd5b50519750600089116118a05760006118ad565b888b8115156118ab57fe5b045b965060008a116118be5760006118d1565b6118d18b670de0b6b3a76400008c612c83565b60018201546002909201549b9d9a9c50989a97999698976001606060020a03606060020a92839004811698908216975060ff928204831696506d01000000000000000000000000009091049091169350915050565b600060066109cf565b6000806000806000806119406123be565b600160028160055460a060020a900460ff16600681111561195d57fe5b148061198a575080600681111561197057fe5b60055460a060020a900460ff16600681111561198857fe5b145b151561199557600080fd5b600954600160a060020a03163314806119b85750600a54600160a060020a031633145b1515611a0e576040805160e560020a62461bcd02815260206004820152601060248201527f4e465f45544f5f554e4b5f544f4b454e00000000000000000000000000000000604482015290519081900360640190fd5b600b548b9850600160a060020a03808a1691161480611a3a5750600c54600160a060020a038c81169116145b15611a4b57611a4889612ca0565b97505b600a54600160a060020a03163314611a6b57611a668a612cc2565b611a6d565b895b9650611a7888612dee565b611a83888c89612e60565b99509950995050505050611a9b888c89878a8f61332d565b9250611aaa8686898d87613545565b600d5460408051600160a060020a038e811682523360208301528183018e9052606082018b9052608082018a905292831660a082015260c081018790529051918a16917f1944d622008ee7d083888039644437ec03dde7f81821e6293c9a7f5c143daf609181900360e00190a25050611b21612512565b505050505050505050565b60055460a060020a900460ff1690565b600d54600160a060020a031690565b6000806060600080600080600280549050111515611b6857600080fd5b600280546000198101935083908110611b7d57fe5b906000526020600020906003020190508060000160009054906101000a9004600160a060020a031681600101548260020184818054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c445780601f10611c1957610100808354040283529160200191611c44565b820191906000526020600020905b815481529060010190602001808311611c2757829003601f168201915b505050505091509550955095509550505090919293565b600080600080600080600080600080611c726123be565b601c60008e600160a060020a0316600160a060020a031681526020019081526020016000209150601860009054906101000a9004600160a060020a0316600160a060020a031663c78baf888e601d60009054906101000a90046001607060020a03168560000160009054906101000a90046001606060020a03168f60016006811115611cfa57fe5b611d02611b2c565b6006811115611d0d57fe5b6040805160e060020a63ffffffff8a16028152600160a060020a0390971660048801526001607060020a0390951660248701526001606060020a03909316604486015260648501919091521460848301525160a48083019260c09291908290030181600087803b158015611d8057600080fd5b505af1158015611d94573d6000803e3d6000fd5b505050506040513d60c0811015611daa57600080fd5b508051602082015160408301516060840151608085015160a090950151939e50919c509a50985090965090508980611ddf57508b5b99508b1515611e8557600854604080517fa8c17ec2000000000000000000000000000000000000000000000000000000008152600481018e90529051611e8192600160a060020a03169163a8c17ec29160248083019260209291908290030181600087803b158015611e5057600080fd5b505af1158015611e64573d6000803e3d6000fd5b505050506040513d6020811015611e7a57600080fd5b505161371b565b9550505b611ea66001611e92611b2c565b6006811115611e9d57fe5b1487838e61374b565b93508a9250611eb3612512565b50509397509397509397509397565b600154600160a060020a031690565b611ed9615362565b33611ee3816137db565b1515611eee57600080fd5b60408051606081018252338152426020808301918252928201868152600280546001810180835560009290925284517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace6003909202918201805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0390921691909117815593517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf8201559151805194975090948794611fd1937f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ad00192909101906152c9565b505050507fe9835ee18f0f0b190604da3474d67a8f29aba2c92e90eee2bdaeca67d40d5a6b33846040518083600160a060020a0316600160a060020a0316815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561204d578181015183820152602001612035565b50505050905090810190601f16801561207a5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a1505050565b600054600160a060020a031690565b610e166123be565b60006120ae6123be565b60038060055460a060020a900460ff1660068111156120c957fe5b146120d357600080fd5b601754600160a060020a031633146120ea57600080fd5b826040516020018082805190602001908083835b6020831061211d5780518252601f1990920191602091820191016120fe565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106121805780518252601f199092019160209182019101612161565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902091508160001916602260405160200180828054600181600116156101000203166002900480156122145780601f106121f2576101008083540402835291820191612214565b820191906000526020600020905b815481529060010190602001808311612200575b50509150506040516020818303038152906040526040518082805190602001908083835b602083106122575780518252601f199092019160209182019101612238565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600019161415156122df576040805160e560020a62461bcd02815260206004820152600b60248201527f4e465f494e565f48415348000000000000000000000000000000000000000000604482015290519081900360640190fd5b601e8290556016546040805133808252600160a060020a039093166020808301829052606093830184815288519484019490945287517fe2df1737b6adc6b62de98ccbcb7709398f71dbfba25133fe52ee1f0a42955ea695949293899390929091608084019185019080838360005b8381101561236657818101518382015260200161234e565b50505050905090810190601f1680156123935780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1506123ab612512565b5050565b601654600160a060020a031690565b60065460009063ffffffff1615156123d557610b0d565b5042600060055460a060020a900460ff1660068111156123f157fe5b14801561240757506124036001612567565b8110155b15612416576124166001613807565b600160055460a060020a900460ff16600681111561243057fe5b14801561244657506124426002612567565b8110155b15612455576124556002613807565b600260055460a060020a900460ff16600681111561246f57fe5b14801561248557506124816003612567565b8110155b15612494576124946003613807565b600360055460a060020a900460ff1660068111156124ae57fe5b1480156124c457506124c06004612567565b8110155b156124d3576124d36006613807565b600460055460a060020a900460ff1660068111156124ed57fe5b14801561250357506124ff6005612567565b8110155b15610b0d57610b0d6005613807565b60055460009061252b9060a060020a900460ff16613aa5565b905080600681111561253957fe5b60055460a060020a900460ff16600681111561255157fe5b14610b0d5761255f81613807565b610b0d612512565b600080808084600681111561257857fe5b1415612587576000925061274d565b600684600681111561259557fe5b141561261f578360068111156125a757fe5b60055460a060020a900460ff1660068111156125bf57fe5b146125cb576000612612565b60055460069060a060020a900460ff16818111156125e557fe5b63ffffffff16600781106125f557fe5b600891828204019190066004029054906101000a900463ffffffff165b63ffffffff16925061274d565b60055460a060020a900460ff16600681111561263757fe5b63ffffffff16600185600681111561264b57fe5b0363ffffffff1611151561267c576006600185600681111561266957fe5b0363ffffffff166007811015156125f557fe5b60055460069060a060020a900460ff168181111561269657fe5b63ffffffff16600781106126a657fe5b600891828204019190066004029054906101000a900463ffffffff1663ffffffff169150600560149054906101000a900460ff1660068111156126e557fe5b60010163ffffffff1690505b8360068111156126fd57fe5b63ffffffff1681101561274957600480548290811061271857fe5b6000918252602090912060088204015460079091166004026101000a900463ffffffff1691909101906001016126f1565b8192505b5050919050565b6000816127648460028304613b33565b81151561276d57fe5b049392505050565b600160a060020a0381166000908152601c602052604090206002810154606060020a900460ff16156127a6576123ab565b80546001606060020a031615156127bc576123ab565b600281018054606060020a6cff000000000000000000000000199091168117909155815460009190046001606060020a0316111561288c576008548154604080517ffb932108000000000000000000000000000000000000000000000000000000008152600160a060020a038681166004830152606060020a9093046001606060020a031660248201529051919092169163fb93210891604480830192600092919082900301818387803b15801561287357600080fd5b505af1158015612887573d6000803e3d6000fd5b505050505b600181015460006001606060020a03909116111561293957600d546001820154604080517f158a4988000000000000000000000000000000000000000000000000000000008152600160a060020a0386811660048301526001606060020a0390931660248201529051919092169163158a498891604480830192600092919082900301818387803b15801561292057600080fd5b505af1158015612934573d6000803e3d6000fd5b505050505b60028101546d0100000000000000000000000000900460ff1615612a5657600b54604080517fc884ef83000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301529151919092169163c884ef8391602480830192600092919082900301818387803b1580156129be57600080fd5b505af11580156129d2573d6000803e3d6000fd5b5050600c54604080517fc884ef83000000000000000000000000000000000000000000000000000000008152600160a060020a038781166004830152915191909216935063c884ef839250602480830192600092919082900301818387803b158015612a3d57600080fd5b505af1158015612a51573d6000803e3d6000fd5b505050505b600d5460018201548254604080516001606060020a039384168152606060020a90920490921660208201528151600160a060020a03938416938616927f862cb185100e8f2a997e72266e5355d5e9baeba4561a0db8ef920bee239a90d2928290030190a35050565b600160a060020a0381166000908152601c602052604090206002810154606060020a900460ff1615612aef576123ab565b80546001606060020a03161515612b05576123ab565b6002810180546cff0000000000000000000000001916606060020a908117918290556001830154600b54600954612b6e9487946001606060020a03940493909316926d0100000000000000000000000000900460ff1691600160a060020a039081169116613b49565b6002810154600c54600a54612bb29285926001606060020a038216926d010000000000000000000000000090920460ff1691600160a060020a039182169116613b49565b6009546001820154604080516001606060020a03606060020a90930492909216825251600160a060020a03928316928516917fd20045a834c447e2a20af3b1a991192581776523441904009bf5ecc742cdd49b919081900360200190a3600a546002820154604080516001606060020a03909216825251600160a060020a03928316928516917fd20045a834c447e2a20af3b1a991192581776523441904009bf5ecc742cdd49b919081900360200190a35050565b6006805463ffffffff191663ffffffff92909216919091179055565b6000612c98612c928585613dc5565b83612754565b949350505050565b8051600090601414612cb157600080fd5b5060140151600160a060020a031690565b600e54600954600a54604080517fbaaa61be000000000000000000000000000000000000000000000000000000008152600160a060020a039384166004820152918316602483015280516000948594859491169263baaa61be9260448084019382900301818787803b158015612d3757600080fd5b505af1158015612d4b573d6000803e3d6000fd5b505050506040513d6040811015612d6157600080fd5b5080516020909101516013549193509150608060020a90046fffffffffffffffffffffffffffffffff164282900310612de4576040805160e560020a62461bcd02815260206004820152601760248201527f4e465f45544f5f494e56414c49445f4554485f52415445000000000000000000604482015290519081900360640190fd5b612c988483613de9565b600160a060020a0381166000908152600360205260409020541515610b0d57600254600010612e1c57600080fd5b600160a060020a038116600081815260036020526040808220439055517f8c41d101e4d957423a65fda82dcc88bc6b3e756166d2331f663c10166658ebb89190a250565b600160a060020a0383166000908152601c60205260408120819081908190819081908190816001612e8f611b2c565b6006811115612e9a57fe5b149050601860009054906101000a9004600160a060020a0316600160a060020a031663c78baf888d601d60009054906101000a90046001607060020a03168560000160009054906101000a90046001606060020a03168e866040518663ffffffff1660e060020a0281526004018086600160a060020a0316600160a060020a03168152602001856001607060020a03168152602001846001606060020a03168152602001838152602001821515151581526020019550505050505060c060405180830381600087803b158015612f6f57600080fd5b505af1158015612f83573d6000803e3d6000fd5b505050506040513d60c0811015612f9957600080fd5b508051602082015160408301516060840151608085015160a090950151939d50919b5099509750909550935087151561301c576040805160e560020a62461bcd02815260206004820152601760248201527f4e465f45544f5f494e565f4e4f545f454c494749424c45000000000000000000604482015290519081900360640190fd5b81546001606060020a03168a0187118015906130385750600085115b151561308e576040805160e560020a62461bcd02815260206004820152601160248201527f4e465f45544f5f4d494e5f5449434b4554000000000000000000000000000000604482015290519081900360640190fd5b81546001606060020a03168a018610156130f2576040805160e560020a62461bcd02815260206004820152601160248201527f4e465f45544f5f4d41585f5449434b4554000000000000000000000000000000604482015290519081900360640190fd5b6130fe8186868d61374b565b15613153576040805160e560020a62461bcd02815260206004820152601260248201527f4e465f45544f5f4d41585f544f4b5f4341500000000000000000000000000000604482015290519081900360640190fd5b80156131cd57888061317757508b600160a060020a03168b600160a060020a031614155b15156131cd576040805160e560020a62461bcd02815260206004820152601060248201527f4e465f45544f5f4e4f545f4f4e5f574c00000000000000000000000000000000604482015290519081900360640190fd5b8b600160a060020a03168b600160a060020a031614156132a057600854604080517f9260faf8000000000000000000000000000000000000000000000000000000008152600481018d9052905161324f92600160a060020a031691639260faf89160248083019260209291908290030181600087803b158015611e5057600080fd5b93505060008311156132a0576013546000196fffffffffffffffffffffffffffffffff9182160116831161327f57fe5b6013546000196fffffffffffffffffffffffffffffffff9182160116909203915b600d54604080517fa5820daa000000000000000000000000000000000000000000000000000000008152600481018890529051600160a060020a039092169163a5820daa9160248082019260009290919082900301818387803b15801561330657600080fd5b505af115801561331a573d6000803e3d6000fd5b5050505050509397509397509397909450565b600160a060020a0386166000908152601c6020526040812080546001606060020a03168015929087019080808080606060020a86106133b6576040805160e560020a62461bcd02815260206004820152601960248201527f4e465f5449434b45545f455843454544535f4d41585f45555200000000000000604482015290519081900360640190fd5b8a87600001600c9054906101000a90046001606060020a0316019450898760010160009054906101000a90046001606060020a031601935086600101600c9054906101000a90046001606060020a031692508660020160009054906101000a90046001606060020a03169150600a60009054906101000a9004600160a060020a0316600160a060020a031633600160a060020a0316141561345a579088019061345f565b918801915b60028701546d0100000000000000000000000000900460ff168061349557508c600160a060020a03168e600160a060020a031614155b87546bffffffffffffffffffffffff199081166001606060020a039889161777ffffffffffffffffffffffff00000000000000000000000019908116606060020a988a168902178a5560018a0180548316978a169790971716948816909602939093179093556002909501805490931694909316939093176dff0000000000000000000000000019166d010000000000000000000000000092151592909202919091179055509695505050505050565b601d54607060020a900466ffffffffffffff168501600080808080670100000000000000861061357157fe5b601d547501000000000000000000000000000000000000000000900466ffffffffffffff168a019450866135a65760006135a9565b60015b601d5460ff9190911660e060020a820463ffffffff160194506001607060020a031689019250607060020a83106135dc57fe5b5050601f54600a546001607060020a0380831692607060020a90041690600160a060020a0316331415613610578701613615565b908701905b601d805474ffffffffffffff00000000000000000000000000001916607060020a66ffffffffffffff9889168102919091177fffffffff00000000000000ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000009790981696909602969096177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e060020a63ffffffff9590951694909402939093176dffffffffffffffffffffffffffff199081166001607060020a0393841617909555601f8054909516908216177bffffffffffffffffffffffffffff0000000000000000000000000000191691169091021790555050505050565b60135460009081906fffffffffffffffffffffffffffffffff168381151561373f57fe5b04915050808203915091565b601054601d54607060020a900466ffffffffffffff1684011184801561376f575080155b156137af5750601154601d5466ffffffffffffff750100000000000000000000000000000000000000000082048116607060020a90920416850103839003115b801580156137cb5750601454601d546001607060020a03168301115b15612c9857506001949350505050565b601754600090600160a060020a03838116911614801561176e57506138006001612567565b1592915050565b60055460a060020a900460ff166000806138218385613dfe565b60058054919350839174ff0000000000000000000000000000000000000000191660a060020a83600681111561385357fe5b0217905550600683600681111561386657fe5b6007811061387057fe5b600891828204019190066004029054906101000a900463ffffffff1690508063ffffffff164263ffffffff1610156138a55750425b8060068460068111156138b457fe5b600781106138be57fe5b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555060048260068111156138f557fe5b815481106138ff57fe5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168101600683600681111561393557fe5b6007811061393f57fe5b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055506139728383613e3b565b81600681111561397e57fe5b60055460a060020a900460ff16600681111561399657fe5b1461399d57fe5b6005546040517f80df1f70000000000000000000000000000000000000000000000000000000008152600160a060020a03909116906380df1f709085908590600401808360068111156139ec57fe5b60ff168152602001826006811115613a0057fe5b60ff16815260200192505050600060405180830381600087803b158015613a2657600080fd5b505af1158015613a3a573d6000803e3d6000fd5b505050507f1076343a2d116e6b4937d6f4987a4dc2df059fcedd58ffd950bca6b443a0a188836006811115613a6b57fe5b836006811115613a7757fe5b6040805163ffffffff938416815291831660208301529184168183015290519081900360600190a150505050565b600080613abd6000601254600101600060155461374b565b90508015613aff576001836006811115613ad357fe5b1415613ae25760029150613b2d565b6002836006811115613af057fe5b1415613aff5760039150613b2d565b6003836006811115613b0d57fe5b148015613b1b5750601e5415155b15613b295760049150613b2d565b8291505b50919050565b600082820183811015613b4257fe5b9392505050565b600080851515613b5857613dbc565b8591508415613d1857604080517ff6ff810e000000000000000000000000000000000000000000000000000000008152306004820152600160a060020a03898116602483015282519087169263f6ff810e92604480820193918290030181600087803b158015613bc757600080fd5b505af1158015613bdb573d6000803e3d6000fd5b505050506040513d6040811015613bf157600080fd5b5051905081811115613bff57fe5b6000811115613d185782600160a060020a031663095ea7b385836040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015613c6b57600080fd5b505af1158015613c7f573d6000803e3d6000fd5b505050506040513d6020811015613c9557600080fd5b50511515613c9f57fe5b83600160a060020a031663c033a490886040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050600060405180830381600087803b158015613cfa57600080fd5b505af1158015613d0e573d6000803e3d6000fd5b5050505080820391505b6000821115613dbc576040805160e160020a635f22feb1028152600160a060020a038981166004830152602482018590526060604483015260006064830181905292519086169263be45fd629260a480820193602093909283900390910190829087803b158015613d8857600080fd5b505af1158015613d9c573d6000803e3d6000fd5b505050506040513d6020811015613db257600080fd5b50511515613dbc57fe5b50505050505050565b6000828202831580613de15750828482811515613dde57fe5b04145b1515613b4257fe5b6000613b428383670de0b6b3a7640000612c83565b60006003826006811115613e0e57fe5b148015613e2e5750600f54601d54607060020a900466ffffffffffffff16105b15613b2d5750600661176e565b6003816006811115613e4957fe5b1415613e5757613e57613eab565b6004816006811115613e6557fe5b1415613e7357613e7361463c565b6006816006811115613e8157fe5b1415613e8f57613e8f614aba565b6005816006811115613e9d57fe5b14156123ab576123ab614d47565b6000806000806000806000806000601860009054906101000a9004600160a060020a0316600160a060020a031663c0c795676040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613f0c57600080fd5b505af1158015613f20573d6000803e3d6000fd5b505050506040513d6020811015613f3657600080fd5b5051604080517f436cdb030000000000000000000000000000000000000000000000000000000081529051919a50600160a060020a038b169163436cdb03916004808201926020929091908290030181600087803b158015613f9757600080fd5b505af1158015613fab573d6000803e3d6000fd5b505050506040513d6020811015613fc157600080fd5b5051601054601d54919950607060020a90910466ffffffffffffff16141561405b5788600160a060020a0316630216e3046040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561402157600080fd5b505af1158015614035573d6000803e3d6000fd5b505050506040513d602081101561404b57600080fd5b5051601054810397509550614136565b601a54601d54604080517f9ae0f890000000000000000000000000000000000000000000000000000000008152607060020a90920466ffffffffffffff16600483015251600160a060020a0390921691639ae0f890916024808201926020929091908290030181600087803b1580156140d357600080fd5b505af11580156140e7573d6000803e3d6000fd5b505050506040513d60208110156140fd57600080fd5b5051601d549097508890607060020a900466ffffffffffffff16880181151561412257fe5b069450600085111561413657848803870196505b601d54607060020a900466ffffffffffffff1687019350878481151561415857fe5b6020805467ffffffffffffffff8b811668010000000000000000026fffffffffffffffff000000000000000019959094041667ffffffffffffffff199091161792909216178155601a54601f54604080517f0cbab4f70000000000000000000000000000000000000000000000000000000081526001607060020a03909216600483015251600160a060020a0390921692630cbab4f7926024808401938290030181600087803b15801561420b57600080fd5b505af115801561421f573d6000803e3d6000fd5b505050506040513d602081101561423557600080fd5b5051602080547fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff16608060020a6001607060020a0393841602178155601a54601f54604080517f0cbab4f7000000000000000000000000000000000000000000000000000000008152607060020a90920490941660048201529251600160a060020a0390911692630cbab4f79260248083019391928290030181600087803b1580156142e057600080fd5b505af11580156142f4573d6000803e3d6000fd5b505050506040513d602081101561430a57600080fd5b5051602180546dffffffffffffffffffffffffffff199081166001607060020a03938416179182905560208054601f80547bffffffffffffffffffffffffffff000000000000000000000000000019948116608060020a8404881691881691909103871617938416948616607060020a9485900487160390951690920292909217909255604080517ff5ee5904000000000000000000000000000000000000000000000000000000008152905167ffffffffffffffff90931692600160a060020a038d169263f5ee590492600480820193918290030181600087803b1580156143f257600080fd5b505af1158015614406573d6000803e3d6000fd5b505050506040513d602081101561441c57600080fd5b5051601f5491029350614440908490607060020a90046001607060020a03166152b3565b600a546017546040805160e160020a635f22feb1028152600160a060020a0392831660048201526001607060020a0385166024820152606060448201526000606482018190529151949650919092169263be45fd629260a480840193602093929083900390910190829087803b1580156144b957600080fd5b505af11580156144cd573d6000803e3d6000fd5b505050506040513d60208110156144e357600080fd5b505115156144ed57fe5b601f80546001607060020a03607060020a8083048216869003909116027bffffffffffffffffffffffffffff00000000000000000000000000001990911617905560208054604080517fc46ca0d8000000000000000000000000000000000000000000000000000000008152905167ffffffffffffffff90921692600160a060020a038d169263c46ca0d89260048082019392918290030181600087803b15801561459757600080fd5b505af11580156145ab573d6000803e3d6000fd5b505050506040513d60208110156145c157600080fd5b50516017546016546020805460408051600160a060020a039586168152939094169183019190915267ffffffffffffffff1681830152929091026060830181905290519092507fa4cfdddd325124fc2d9f4fa352e25b682cbbb596be1c4a9523c423f0c308bef99181900360800190a1505050505050505050565b600854604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290516000928392600160a060020a03909116916370a082319160248082019260209290919082900301818787803b1580156146a657600080fd5b505af11580156146ba573d6000803e3d6000fd5b505050506040513d60208110156146d057600080fd5b505191506146dd8261371b565b50600854601b546040805160e160020a635f22feb1028152600160a060020a03928316600482015260248101859052606060448201526000606482018190529151949550919092169263be45fd629260a480840193602093929083900390910190829087803b15801561474f57600080fd5b505af1158015614763573d6000803e3d6000fd5b505050506040513d602081101561477957600080fd5b5051151561478357fe5b601f5460006001607060020a03909116111561484657600954601654601f546040805160e160020a635f22feb1028152600160a060020a0393841660048201526001607060020a039092166024830152606060448301526000606483018190529051929093169263be45fd629260a4808401936020939083900390910190829087803b15801561481257600080fd5b505af1158015614826573d6000803e3d6000fd5b505050506040513d602081101561483c57600080fd5b5051151561484657fe5b601f546000607060020a9091046001607060020a0316111561491557600a54601654601f546040805160e160020a635f22feb1028152600160a060020a039384166004820152607060020a9092046001607060020a03166024830152606060448301526000606483018190529051929093169263be45fd629260a4808401936020939083900390910190829087803b1580156148e157600080fd5b505af11580156148f5573d6000803e3d6000fd5b505050506040513d602081101561490b57600080fd5b5051151561491557fe5b600d54602054604080517fa5820daa0000000000000000000000000000000000000000000000000000000081526801000000000000000090920467ffffffffffffffff16600483015251600160a060020a039092169163a5820daa9160248082019260009290919082900301818387803b15801561499257600080fd5b505af11580156149a6573d6000803e3d6000fd5b5050601b5460408051600160a060020a03909216825260208201869052818101859052517fe540df3afae3936e26f26bbdb7d7eba37fb1c41ccc19d2da9c8191869e85dc3a9350908190036060019150a1601654600954601f5460408051600160a060020a0394851681529290931660208301526001607060020a03168183015290517feb5dfd984307753c74358459c54dbedb2525db2c9b1c4c8eee9db382b1e391c59181900360600190a1601654600a54601f5460408051600160a060020a039485168152939092166020840152607060020a90046001607060020a031682820152517feb5dfd984307753c74358459c54dbedb2525db2c9b1c4c8eee9db382b1e391c5916060908290030190a15050565b600854604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290516000928392600160a060020a03909116916370a082319160248082019260209290919082900301818787803b158015614b2457600080fd5b505af1158015614b38573d6000803e3d6000fd5b505050506040513d6020811015614b4e57600080fd5b5051600d54604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051929450600160a060020a03909116916370a08231916024808201926020929091908290030181600087803b158015614bb957600080fd5b505af1158015614bcd573d6000803e3d6000fd5b505050506040513d6020811015614be357600080fd5b505190506000821115614c6f57600854604080517f42966c68000000000000000000000000000000000000000000000000000000008152600481018590529051600160a060020a03909216916342966c689160248082019260009290919082900301818387803b158015614c5657600080fd5b505af1158015614c6a573d6000803e3d6000fd5b505050505b6000811115614cf757600d54604080517f67fbd289000000000000000000000000000000000000000000000000000000008152600481018490529051600160a060020a03909216916367fbd2899160248082019260009290919082900301818387803b158015614cde57600080fd5b505af1158015614cf2573d6000803e3d6000fd5b505050505b600d5460408051600160a060020a03909216825260208201839052818101849052517f8d5b2366bddfdb8f9f6e9b9f609d5bcc78a8633d87dae106f4e9888187275ed19181900360600190a15050565b6000806060600760009054906101000a9004600160a060020a0316600160a060020a031663494115576040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015614d9f57600080fd5b505af1158015614db3573d6000803e3d6000fd5b505050506040513d6020811015614dc957600080fd5b50519250600160a060020a0383161515614ddf57fe5b600760009054906101000a9004600160a060020a0316600160a060020a031663f40a80ea6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015614e3257600080fd5b505af1158015614e46573d6000803e3d6000fd5b505050506040513d6020811015614e5c57600080fd5b50519150600160a060020a0382161515614e7257fe5b5060085460408051600160a060020a03909216606060020a02602080840191909152815160148185030181526034909301909152546000608060020a9091046001607060020a03161115614fcd576009546020805460405160e160020a635f22feb1028152600160a060020a0387811660048301908152608060020a9093046001607060020a031660248301819052606060448401908152875160648501528751929096169563be45fd62958a959294899460849091019185019080838360005b83811015614f4b578181015183820152602001614f33565b50505050905090810190601f168015614f785780820380516001836020036101000a031916815260200191505b50945050505050602060405180830381600087803b158015614f9957600080fd5b505af1158015614fad573d6000803e3d6000fd5b505050506040513d6020811015614fc357600080fd5b50511515614fcd57fe5b60215460006001607060020a0390911611156150ec57600a5460215460405160e160020a635f22feb1028152600160a060020a03868116600483019081526001607060020a0390931660248301819052606060448401908152865160648501528651929095169463be45fd629489949293889391929160840190602085019080838360005b8381101561506a578181015183820152602001615052565b50505050905090810190601f1680156150975780820380516001836020036101000a031916815260200191505b50945050505050602060405180830381600087803b1580156150b857600080fd5b505af11580156150cc573d6000803e3d6000fd5b505050506040513d60208110156150e257600080fd5b505115156150ec57fe5b600d54602054604080517f158a4988000000000000000000000000000000000000000000000000000000008152600160a060020a0386811660048301526801000000000000000090930467ffffffffffffffff1660248201529051919092169163158a498891604480830192600092919082900301818387803b15801561517257600080fd5b505af1158015615186573d6000803e3d6000fd5b50506009546020805460408051600160a060020a039485168152938916928401929092526001607060020a03608060020a9091041682820152517f6e6fd9b5ea7bbd5055657dcdd81818e67183bd59bcfb929db8da7a130b9f37c89350908190036060019150a1600a5460215460408051600160a060020a03938416815292861660208401526001607060020a0390911682820152517f6e6fd9b5ea7bbd5055657dcdd81818e67183bd59bcfb929db8da7a130b9f37c89181900360600190a1600d546020805460408051600160a060020a0394851681529386169284019290925267ffffffffffffffff680100000000000000009091041682820152517f3d5f381e0481c18a6e87775195ba5c8e32f97f48426ad47b7eca0eb6828bcbe79181900360600190a1505050565b60008183106152c25781613b42565b5090919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061530a57805160ff1916838001178555615337565b82800160010185558215615337579182015b8281111561533757825182559160200191906001019061531c565b50610b80929150615381565b60e0604051908101604052806007906020820280388339509192915050565b6040805160608181018352600080835260208301529181019190915290565b6109a691905b80821115610b8057600081556001016153875600a165627a7a7230582056a74eca5a01dd4fa58a7c81c8c45ebe3df29b0fa05f3d1740f03e897744add5002900000000000000000000000082fb5126506b6c315fa4a7ae3d4cb8a46a1aae67000000000000000000000000f3f35c09a8e9de32a8d60451bd80e5381cd8f52d000000000000000000000000b8a93fdc69df45c59302fe867877786a5e05be050000000000000000000000007ddf45e48fd60b736f2c45252c14f7da7c88310d000000000000000000000000535bfaeb50580f674bd2e076d6073adf28a46fa8
Deployed Bytecode
0x6080604052600436106101be5763ffffffff60e060020a60003504166304f36f5981146101c35780630b93381b146101fc57806310716b251461022557806310ea13df14610280578063168250b6146102b357806320f99c0a14610300578063302fe516146103315780633d57f3ca1461034657806345067cfb1461035b578063468e4c73146103705780634e71d92d146103c657806357875631146103db578063590e1ae3146104025780635aa2b2061461041757806361c217811461042c57806363bd1d4a146104565780636b4158df1461046b5780636fa00f07146104f55780637b154313146105285780638291286c1461053d57806382dfe1501461056b57806388d73770146105ab5780638fb29d6c14610600578063a0f8be8c146106b7578063b3f05b97146106d2578063b568dfc0146106e7578063ba414fa61461075c578063c0ee0b8a14610771578063c19d93fb146107da578063c6b43514146107ef578063c90f793e14610804578063e7ee7ced14610819578063ea490b8414610887578063eb4e64d61461089c578063f5d60a51146108f5578063f71a78431461090a578063fa29b3e31461091f578063fdac7dfe14610978575b600080fd5b3480156101cf57600080fd5b506101d861098d565b604051808260068111156101e857fe5b60ff16815260200191505060405180910390f35b34801561020857600080fd5b506102116109a9565b604080519115158252519081900360200190f35b34801561023157600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261027e9436949293602493928401919081908401838280828437509497506109ee9650505050505050565b005b34801561028c57600080fd5b50610295610b10565b60408051938452602084019290925282820152519081900360600190f35b3480156102bf57600080fd5b506102c8610b3f565b604051808260e080838360005b838110156102ed5781810151838201526020016102d5565b5050505090500191505060405180910390f35b34801561030c57600080fd5b50610315610b84565b60408051600160a060020a039092168252519081900360200190f35b34801561033d57600080fd5b50610315610b93565b34801561035257600080fd5b50610315610ba2565b34801561036757600080fd5b50610315610bb1565b34801561037c57600080fd5b50610385610bc0565b604080519889526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b3480156103d257600080fd5b5061027e610da6565b3480156103e757600080fd5b5061027e600160a060020a0360043581169060243516610e18565b34801561040e57600080fd5b5061027e611075565b34801561042357600080fd5b506103156110b4565b34801561043857600080fd5b5061027e600160a060020a03600435811690602435166044356110c3565b34801561046257600080fd5b5061027e6113d2565b34801561047757600080fd5b506104806113ff565b6040805160208082528351818301528351919283929083019185019080838360005b838110156104ba5781810151838201526020016104a2565b50505050905090810190601f1680156104e75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561050157600080fd5b50610516600160a060020a0360043516611495565b60408051918252519081900360200190f35b34801561053457600080fd5b506105166114b0565b34801561054957600080fd5b506105526114b6565b6040805192835260208301919091528051918290030190f35b34801561057757600080fd5b506105806114dd565b60408051600160a060020a039485168152928416602084015292168183015290519081900360600190f35b3480156105b757600080fd5b506040805160206004803580820135838102808601850190965280855261027e953695939460249493850192918291850190849080828437509497506114fa9650505050505050565b34801561060c57600080fd5b5061061860043561166e565b6040518085600160a060020a0316600160a060020a0316815260200184815260200180602001838152602001828103825284818151815260200191508051906020019080838360005b83811015610679578181015183820152602001610661565b50505050905090810190601f1680156106a65780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b3480156106c357600080fd5b5061051660ff60043516611763565b3480156106de57600080fd5b50610211611774565b3480156106f357600080fd5b50610708600160a060020a03600435166117bf565b604080519a8b5260208b0199909952898901979097526060890195909552608088019390935260a087019190915260c086015260e08501521515610100840152151561012083015251908190036101400190f35b34801561076857600080fd5b50610211611926565b34801561077d57600080fd5b50604080516020600460443581810135601f810184900484028501840190955284845261027e948235600160a060020a031694602480359536959460649492019190819084018382808284375094975061192f9650505050505050565b3480156107e657600080fd5b506101d8611b2c565b3480156107fb57600080fd5b50610315611b3c565b34801561081057600080fd5b50610618611b4b565b34801561082557600080fd5b50610842600160a060020a03600435166024351515604435611c5b565b6040805198151589529615156020890152878701959095526060870193909352608086019190915260a0850152151560c084015260e083015251908190036101000190f35b34801561089357600080fd5b50610315611ec2565b3480156108a857600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261027e943694929360249392840191908190840183828082843750949750611ed19650505050505050565b34801561090157600080fd5b5061031561208d565b34801561091657600080fd5b5061027e61209c565b34801561092b57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261027e9436949293602493928401919081908401838280828437509497506120a49650505050505050565b34801561098457600080fd5b506103156123af565b60006109976123be565b5060055460a060020a900460ff165b90565b6000600460055460a060020a900460ff1660068111156109c557fe5b14806109e9575060055b60055460a060020a900460ff1660068111156109e757fe5b145b905090565b6109f66123be565b60038060055460a060020a900460ff166006811115610a1157fe5b14610a1b57600080fd5b601654600160a060020a03163314610a3257600080fd5b8151610a459060229060208501906152c9565b506017546040805133808252600160a060020a039093166020808301829052606093830184815287519484019490945286517f17d12ed04f5e56c750cbda26b99121d3068cfa151b827a8c4f9a0bcdaeb32cf495949293889390929091608084019185019080838360005b83811015610ac8578181015183820152602001610ab0565b50505050905090810190601f168015610af55780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a150610b0d612512565b50565b601d546001607060020a03811691607060020a820466ffffffffffffff169160e060020a900463ffffffff1690565b610b47615343565b60005b6007811015610b8057610b67816006811115610b6257fe5b612567565b828260078110610b7357fe5b6020020152600101610b4a565b5090565b601754600160a060020a031690565b601854600160a060020a031690565b600554600160a060020a031690565b601b54600160a060020a031681565b600080600080600080600080602060009054906101000a900467ffffffffffffffff16601860009054906101000a9004600160a060020a0316600160a060020a031663c0c795676040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015610c3657600080fd5b505af1158015610c4a573d6000803e3d6000fd5b505050506040513d6020811015610c6057600080fd5b5051604080517fc46ca0d80000000000000000000000000000000000000000000000000000000081529051600160a060020a039092169163c46ca0d8916004808201926020929091908290030181600087803b158015610cbf57600080fd5b505af1158015610cd3573d6000803e3d6000fd5b505050506040513d6020811015610ce957600080fd5b5051602054601f5460215467ffffffffffffffff808416948502946001607060020a0380851695607060020a90950481169468010000000000000000810490931693608060020a909304811692169015610d6557601d54602054610d60916001607060020a03169067ffffffffffffffff16612754565b610d68565b60005b67ffffffffffffffff9788169f50959d506001607060020a039485169c509284169a5094169750928116955091909116925090509091929394959697565b610dae6123be565b600460058160055460a060020a900460ff166006811115610dcb57fe5b1480610df85750806006811115610dde57fe5b60055460a060020a900460ff166006811115610df657fe5b145b1515610e0357600080fd5b610e0c33612775565b5050610e16612512565b565b60008054604080517f9085b77f0000000000000000000000000000000000000000000000000000000081523360048201527fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da602482018190523060448301527bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19853516606483015291519192600160a060020a031691639085b77f9160848082019260209290919082900301818887803b158015610ed257600080fd5b505af1158015610ee6573d6000803e3d6000fd5b505050506040513d6020811015610efc57600080fd5b50511515610f0957600080fd5b604080517f9085b77f000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301527fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da6024830152306044830152600080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166064840152925190871692639085b77f92608480820193602093909283900390910190829087803b158015610fc357600080fd5b505af1158015610fd7573d6000803e3d6000fd5b505050506040513d6020811015610fed57600080fd5b50511515610ffa57600080fd5b60008054600160a060020a0386811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040805133815291909216602082018190528183019390935290519193507f7d475c32583df95fccc34a6e12df24c1fc9943092cc129b6512013aecba0f136919081900360600190a150505050565b61107d6123be565b60068060055460a060020a900460ff16600681111561109857fe5b146110a257600080fd5b6110ab33612abe565b50610e16612512565b601954600160a060020a031681565b601654600090600160a060020a031633146110dd57600080fd5b60006110e76114b0565b116110f157600080fd5b6110f96123be565b60008060055460a060020a900460ff16600681111561111457fe5b1461111e57600080fd5b601854600160a060020a0386811691161461113857600080fd5b600d54600160a060020a0385811691161461115257600080fd5b63ffffffff831061115f57fe5b42831180156111ed5750601960009054906101000a9004600160a060020a0316600160a060020a031663cbf9e46a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156111bc57600080fd5b505af11580156111d0573d6000803e3d6000fd5b505050506040513d60208110156111e657600080fd5b5051428403115b1515611243576040805160e560020a62461bcd02815260206004820152601560248201527f4e465f45544f5f444154455f544f4f5f4541524c590000000000000000000000604482015290519081900360640190fd5b61124d6001612567565b91508115806112db5750601960009054906101000a9004600160a060020a0316600160a060020a031663cbf9e46a6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156112aa57600080fd5b505af11580156112be573d6000803e3d6000fd5b505050506040513d60208110156112d457600080fd5b5051428303115b1515611331576040805160e560020a62461bcd02815260206004820152601560248201527f4e465f45544f5f53544152545f544f4f5f534f4f4e0000000000000000000000604482015290519081900360640190fd5b61133a83612c67565b60408051338152600160a060020a03808816602083015286168183015290517fd4a8a704f685c06e20e177cb50c64301b93b8590b5050406fd3095955620becb9181900360600190a1604080513381526020810184905280820185905290517fbbd191f3e264e3b631bed4fb848beb0126e0ec1ca54512ddf0a1cbdf966492869181900360600190a1506113cc612512565b50505050565b6113da6123be565b60058060055460a060020a900460ff1660068111156113f557fe5b146110ab57600080fd5b60228054604080516020601f600260001961010060018816150201909516949094049384018190048102820181019092528281526060939092909183018282801561148b5780601f106114605761010080835404028352916020019161148b565b820191906000526020600020905b81548152906001019060200180831161146e57829003601f168201915b5050505050905090565b600160a060020a031660009081526003602052604090205490565b60025490565b7f70ef68fc8c585f9edc7af1bfac26c4b1b9e98ba05cf5ddd99e4b3dc46ea7007360029091565b601b54600754601a54600160a060020a0392831693918316921690565b6000808060058060055460a060020a900460ff16600681111561151957fe5b1461152357600080fd5b600760009054906101000a9004600160a060020a0316600160a060020a031663494115576040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561157657600080fd5b505af115801561158a573d6000803e3d6000fd5b505050506040513d60208110156115a057600080fd5b50519350600092505b84518310156116675784838151811015156115c057fe5b60209081029091010151600854604080517f813bedf2000000000000000000000000000000000000000000000000000000008152600160a060020a038085166004830152928316602482015261ffff604482015290519294509086169163813bedf29160648082019260009290919082900301818387803b15801561164457600080fd5b505af1158015611658573d6000803e3d6000fd5b505050506001830192506115a9565b5050505050565b600080606060008060028681548110151561168557fe5b906000526020600020906003020190508060000160009054906101000a9004600160a060020a031681600101548260020188818054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561174c5780601f106117215761010080835404028352916020019161174c565b820191906000526020600020905b81548152906001019060200180831161172f57829003601f168201915b505050505091509450945094509450509193509193565b600061176e82612567565b92915050565b6000600660055460a060020a900460ff16600681111561179057fe5b14806117b257506005805460a060020a900460ff1660068111156117b057fe5b145b806109e9575060046109cf565b600160a060020a038082166000908152601c602090815260408083208054600182015460185484517fedf04bd40000000000000000000000000000000000000000000000000000000081526001606060020a0392831660048201819052955183851699606060020a909504909316979596869586958695869586958695919093169263edf04bd4926024808301939282900301818887803b15801561186357600080fd5b505af1158015611877573d6000803e3d6000fd5b505050506040513d602081101561188d57600080fd5b50519750600089116118a05760006118ad565b888b8115156118ab57fe5b045b965060008a116118be5760006118d1565b6118d18b670de0b6b3a76400008c612c83565b60018201546002909201549b9d9a9c50989a97999698976001606060020a03606060020a92839004811698908216975060ff928204831696506d01000000000000000000000000009091049091169350915050565b600060066109cf565b6000806000806000806119406123be565b600160028160055460a060020a900460ff16600681111561195d57fe5b148061198a575080600681111561197057fe5b60055460a060020a900460ff16600681111561198857fe5b145b151561199557600080fd5b600954600160a060020a03163314806119b85750600a54600160a060020a031633145b1515611a0e576040805160e560020a62461bcd02815260206004820152601060248201527f4e465f45544f5f554e4b5f544f4b454e00000000000000000000000000000000604482015290519081900360640190fd5b600b548b9850600160a060020a03808a1691161480611a3a5750600c54600160a060020a038c81169116145b15611a4b57611a4889612ca0565b97505b600a54600160a060020a03163314611a6b57611a668a612cc2565b611a6d565b895b9650611a7888612dee565b611a83888c89612e60565b99509950995050505050611a9b888c89878a8f61332d565b9250611aaa8686898d87613545565b600d5460408051600160a060020a038e811682523360208301528183018e9052606082018b9052608082018a905292831660a082015260c081018790529051918a16917f1944d622008ee7d083888039644437ec03dde7f81821e6293c9a7f5c143daf609181900360e00190a25050611b21612512565b505050505050505050565b60055460a060020a900460ff1690565b600d54600160a060020a031690565b6000806060600080600080600280549050111515611b6857600080fd5b600280546000198101935083908110611b7d57fe5b906000526020600020906003020190508060000160009054906101000a9004600160a060020a031681600101548260020184818054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c445780601f10611c1957610100808354040283529160200191611c44565b820191906000526020600020905b815481529060010190602001808311611c2757829003601f168201915b505050505091509550955095509550505090919293565b600080600080600080600080600080611c726123be565b601c60008e600160a060020a0316600160a060020a031681526020019081526020016000209150601860009054906101000a9004600160a060020a0316600160a060020a031663c78baf888e601d60009054906101000a90046001607060020a03168560000160009054906101000a90046001606060020a03168f60016006811115611cfa57fe5b611d02611b2c565b6006811115611d0d57fe5b6040805160e060020a63ffffffff8a16028152600160a060020a0390971660048801526001607060020a0390951660248701526001606060020a03909316604486015260648501919091521460848301525160a48083019260c09291908290030181600087803b158015611d8057600080fd5b505af1158015611d94573d6000803e3d6000fd5b505050506040513d60c0811015611daa57600080fd5b508051602082015160408301516060840151608085015160a090950151939e50919c509a50985090965090508980611ddf57508b5b99508b1515611e8557600854604080517fa8c17ec2000000000000000000000000000000000000000000000000000000008152600481018e90529051611e8192600160a060020a03169163a8c17ec29160248083019260209291908290030181600087803b158015611e5057600080fd5b505af1158015611e64573d6000803e3d6000fd5b505050506040513d6020811015611e7a57600080fd5b505161371b565b9550505b611ea66001611e92611b2c565b6006811115611e9d57fe5b1487838e61374b565b93508a9250611eb3612512565b50509397509397509397509397565b600154600160a060020a031690565b611ed9615362565b33611ee3816137db565b1515611eee57600080fd5b60408051606081018252338152426020808301918252928201868152600280546001810180835560009290925284517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace6003909202918201805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0390921691909117815593517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf8201559151805194975090948794611fd1937f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ad00192909101906152c9565b505050507fe9835ee18f0f0b190604da3474d67a8f29aba2c92e90eee2bdaeca67d40d5a6b33846040518083600160a060020a0316600160a060020a0316815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561204d578181015183820152602001612035565b50505050905090810190601f16801561207a5780820380516001836020036101000a031916815260200191505b50935050505060405180910390a1505050565b600054600160a060020a031690565b610e166123be565b60006120ae6123be565b60038060055460a060020a900460ff1660068111156120c957fe5b146120d357600080fd5b601754600160a060020a031633146120ea57600080fd5b826040516020018082805190602001908083835b6020831061211d5780518252601f1990920191602091820191016120fe565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106121805780518252601f199092019160209182019101612161565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902091508160001916602260405160200180828054600181600116156101000203166002900480156122145780601f106121f2576101008083540402835291820191612214565b820191906000526020600020905b815481529060010190602001808311612200575b50509150506040516020818303038152906040526040518082805190602001908083835b602083106122575780518252601f199092019160209182019101612238565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600019161415156122df576040805160e560020a62461bcd02815260206004820152600b60248201527f4e465f494e565f48415348000000000000000000000000000000000000000000604482015290519081900360640190fd5b601e8290556016546040805133808252600160a060020a039093166020808301829052606093830184815288519484019490945287517fe2df1737b6adc6b62de98ccbcb7709398f71dbfba25133fe52ee1f0a42955ea695949293899390929091608084019185019080838360005b8381101561236657818101518382015260200161234e565b50505050905090810190601f1680156123935780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1506123ab612512565b5050565b601654600160a060020a031690565b60065460009063ffffffff1615156123d557610b0d565b5042600060055460a060020a900460ff1660068111156123f157fe5b14801561240757506124036001612567565b8110155b15612416576124166001613807565b600160055460a060020a900460ff16600681111561243057fe5b14801561244657506124426002612567565b8110155b15612455576124556002613807565b600260055460a060020a900460ff16600681111561246f57fe5b14801561248557506124816003612567565b8110155b15612494576124946003613807565b600360055460a060020a900460ff1660068111156124ae57fe5b1480156124c457506124c06004612567565b8110155b156124d3576124d36006613807565b600460055460a060020a900460ff1660068111156124ed57fe5b14801561250357506124ff6005612567565b8110155b15610b0d57610b0d6005613807565b60055460009061252b9060a060020a900460ff16613aa5565b905080600681111561253957fe5b60055460a060020a900460ff16600681111561255157fe5b14610b0d5761255f81613807565b610b0d612512565b600080808084600681111561257857fe5b1415612587576000925061274d565b600684600681111561259557fe5b141561261f578360068111156125a757fe5b60055460a060020a900460ff1660068111156125bf57fe5b146125cb576000612612565b60055460069060a060020a900460ff16818111156125e557fe5b63ffffffff16600781106125f557fe5b600891828204019190066004029054906101000a900463ffffffff165b63ffffffff16925061274d565b60055460a060020a900460ff16600681111561263757fe5b63ffffffff16600185600681111561264b57fe5b0363ffffffff1611151561267c576006600185600681111561266957fe5b0363ffffffff166007811015156125f557fe5b60055460069060a060020a900460ff168181111561269657fe5b63ffffffff16600781106126a657fe5b600891828204019190066004029054906101000a900463ffffffff1663ffffffff169150600560149054906101000a900460ff1660068111156126e557fe5b60010163ffffffff1690505b8360068111156126fd57fe5b63ffffffff1681101561274957600480548290811061271857fe5b6000918252602090912060088204015460079091166004026101000a900463ffffffff1691909101906001016126f1565b8192505b5050919050565b6000816127648460028304613b33565b81151561276d57fe5b049392505050565b600160a060020a0381166000908152601c602052604090206002810154606060020a900460ff16156127a6576123ab565b80546001606060020a031615156127bc576123ab565b600281018054606060020a6cff000000000000000000000000199091168117909155815460009190046001606060020a0316111561288c576008548154604080517ffb932108000000000000000000000000000000000000000000000000000000008152600160a060020a038681166004830152606060020a9093046001606060020a031660248201529051919092169163fb93210891604480830192600092919082900301818387803b15801561287357600080fd5b505af1158015612887573d6000803e3d6000fd5b505050505b600181015460006001606060020a03909116111561293957600d546001820154604080517f158a4988000000000000000000000000000000000000000000000000000000008152600160a060020a0386811660048301526001606060020a0390931660248201529051919092169163158a498891604480830192600092919082900301818387803b15801561292057600080fd5b505af1158015612934573d6000803e3d6000fd5b505050505b60028101546d0100000000000000000000000000900460ff1615612a5657600b54604080517fc884ef83000000000000000000000000000000000000000000000000000000008152600160a060020a0385811660048301529151919092169163c884ef8391602480830192600092919082900301818387803b1580156129be57600080fd5b505af11580156129d2573d6000803e3d6000fd5b5050600c54604080517fc884ef83000000000000000000000000000000000000000000000000000000008152600160a060020a038781166004830152915191909216935063c884ef839250602480830192600092919082900301818387803b158015612a3d57600080fd5b505af1158015612a51573d6000803e3d6000fd5b505050505b600d5460018201548254604080516001606060020a039384168152606060020a90920490921660208201528151600160a060020a03938416938616927f862cb185100e8f2a997e72266e5355d5e9baeba4561a0db8ef920bee239a90d2928290030190a35050565b600160a060020a0381166000908152601c602052604090206002810154606060020a900460ff1615612aef576123ab565b80546001606060020a03161515612b05576123ab565b6002810180546cff0000000000000000000000001916606060020a908117918290556001830154600b54600954612b6e9487946001606060020a03940493909316926d0100000000000000000000000000900460ff1691600160a060020a039081169116613b49565b6002810154600c54600a54612bb29285926001606060020a038216926d010000000000000000000000000090920460ff1691600160a060020a039182169116613b49565b6009546001820154604080516001606060020a03606060020a90930492909216825251600160a060020a03928316928516917fd20045a834c447e2a20af3b1a991192581776523441904009bf5ecc742cdd49b919081900360200190a3600a546002820154604080516001606060020a03909216825251600160a060020a03928316928516917fd20045a834c447e2a20af3b1a991192581776523441904009bf5ecc742cdd49b919081900360200190a35050565b6006805463ffffffff191663ffffffff92909216919091179055565b6000612c98612c928585613dc5565b83612754565b949350505050565b8051600090601414612cb157600080fd5b5060140151600160a060020a031690565b600e54600954600a54604080517fbaaa61be000000000000000000000000000000000000000000000000000000008152600160a060020a039384166004820152918316602483015280516000948594859491169263baaa61be9260448084019382900301818787803b158015612d3757600080fd5b505af1158015612d4b573d6000803e3d6000fd5b505050506040513d6040811015612d6157600080fd5b5080516020909101516013549193509150608060020a90046fffffffffffffffffffffffffffffffff164282900310612de4576040805160e560020a62461bcd02815260206004820152601760248201527f4e465f45544f5f494e56414c49445f4554485f52415445000000000000000000604482015290519081900360640190fd5b612c988483613de9565b600160a060020a0381166000908152600360205260409020541515610b0d57600254600010612e1c57600080fd5b600160a060020a038116600081815260036020526040808220439055517f8c41d101e4d957423a65fda82dcc88bc6b3e756166d2331f663c10166658ebb89190a250565b600160a060020a0383166000908152601c60205260408120819081908190819081908190816001612e8f611b2c565b6006811115612e9a57fe5b149050601860009054906101000a9004600160a060020a0316600160a060020a031663c78baf888d601d60009054906101000a90046001607060020a03168560000160009054906101000a90046001606060020a03168e866040518663ffffffff1660e060020a0281526004018086600160a060020a0316600160a060020a03168152602001856001607060020a03168152602001846001606060020a03168152602001838152602001821515151581526020019550505050505060c060405180830381600087803b158015612f6f57600080fd5b505af1158015612f83573d6000803e3d6000fd5b505050506040513d60c0811015612f9957600080fd5b508051602082015160408301516060840151608085015160a090950151939d50919b5099509750909550935087151561301c576040805160e560020a62461bcd02815260206004820152601760248201527f4e465f45544f5f494e565f4e4f545f454c494749424c45000000000000000000604482015290519081900360640190fd5b81546001606060020a03168a0187118015906130385750600085115b151561308e576040805160e560020a62461bcd02815260206004820152601160248201527f4e465f45544f5f4d494e5f5449434b4554000000000000000000000000000000604482015290519081900360640190fd5b81546001606060020a03168a018610156130f2576040805160e560020a62461bcd02815260206004820152601160248201527f4e465f45544f5f4d41585f5449434b4554000000000000000000000000000000604482015290519081900360640190fd5b6130fe8186868d61374b565b15613153576040805160e560020a62461bcd02815260206004820152601260248201527f4e465f45544f5f4d41585f544f4b5f4341500000000000000000000000000000604482015290519081900360640190fd5b80156131cd57888061317757508b600160a060020a03168b600160a060020a031614155b15156131cd576040805160e560020a62461bcd02815260206004820152601060248201527f4e465f45544f5f4e4f545f4f4e5f574c00000000000000000000000000000000604482015290519081900360640190fd5b8b600160a060020a03168b600160a060020a031614156132a057600854604080517f9260faf8000000000000000000000000000000000000000000000000000000008152600481018d9052905161324f92600160a060020a031691639260faf89160248083019260209291908290030181600087803b158015611e5057600080fd5b93505060008311156132a0576013546000196fffffffffffffffffffffffffffffffff9182160116831161327f57fe5b6013546000196fffffffffffffffffffffffffffffffff9182160116909203915b600d54604080517fa5820daa000000000000000000000000000000000000000000000000000000008152600481018890529051600160a060020a039092169163a5820daa9160248082019260009290919082900301818387803b15801561330657600080fd5b505af115801561331a573d6000803e3d6000fd5b5050505050509397509397509397909450565b600160a060020a0386166000908152601c6020526040812080546001606060020a03168015929087019080808080606060020a86106133b6576040805160e560020a62461bcd02815260206004820152601960248201527f4e465f5449434b45545f455843454544535f4d41585f45555200000000000000604482015290519081900360640190fd5b8a87600001600c9054906101000a90046001606060020a0316019450898760010160009054906101000a90046001606060020a031601935086600101600c9054906101000a90046001606060020a031692508660020160009054906101000a90046001606060020a03169150600a60009054906101000a9004600160a060020a0316600160a060020a031633600160a060020a0316141561345a579088019061345f565b918801915b60028701546d0100000000000000000000000000900460ff168061349557508c600160a060020a03168e600160a060020a031614155b87546bffffffffffffffffffffffff199081166001606060020a039889161777ffffffffffffffffffffffff00000000000000000000000019908116606060020a988a168902178a5560018a0180548316978a169790971716948816909602939093179093556002909501805490931694909316939093176dff0000000000000000000000000019166d010000000000000000000000000092151592909202919091179055509695505050505050565b601d54607060020a900466ffffffffffffff168501600080808080670100000000000000861061357157fe5b601d547501000000000000000000000000000000000000000000900466ffffffffffffff168a019450866135a65760006135a9565b60015b601d5460ff9190911660e060020a820463ffffffff160194506001607060020a031689019250607060020a83106135dc57fe5b5050601f54600a546001607060020a0380831692607060020a90041690600160a060020a0316331415613610578701613615565b908701905b601d805474ffffffffffffff00000000000000000000000000001916607060020a66ffffffffffffff9889168102919091177fffffffff00000000000000ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000009790981696909602969096177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e060020a63ffffffff9590951694909402939093176dffffffffffffffffffffffffffff199081166001607060020a0393841617909555601f8054909516908216177bffffffffffffffffffffffffffff0000000000000000000000000000191691169091021790555050505050565b60135460009081906fffffffffffffffffffffffffffffffff168381151561373f57fe5b04915050808203915091565b601054601d54607060020a900466ffffffffffffff1684011184801561376f575080155b156137af5750601154601d5466ffffffffffffff750100000000000000000000000000000000000000000082048116607060020a90920416850103839003115b801580156137cb5750601454601d546001607060020a03168301115b15612c9857506001949350505050565b601754600090600160a060020a03838116911614801561176e57506138006001612567565b1592915050565b60055460a060020a900460ff166000806138218385613dfe565b60058054919350839174ff0000000000000000000000000000000000000000191660a060020a83600681111561385357fe5b0217905550600683600681111561386657fe5b6007811061387057fe5b600891828204019190066004029054906101000a900463ffffffff1690508063ffffffff164263ffffffff1610156138a55750425b8060068460068111156138b457fe5b600781106138be57fe5b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555060048260068111156138f557fe5b815481106138ff57fe5b90600052602060002090600891828204019190066004029054906101000a900463ffffffff168101600683600681111561393557fe5b6007811061393f57fe5b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055506139728383613e3b565b81600681111561397e57fe5b60055460a060020a900460ff16600681111561399657fe5b1461399d57fe5b6005546040517f80df1f70000000000000000000000000000000000000000000000000000000008152600160a060020a03909116906380df1f709085908590600401808360068111156139ec57fe5b60ff168152602001826006811115613a0057fe5b60ff16815260200192505050600060405180830381600087803b158015613a2657600080fd5b505af1158015613a3a573d6000803e3d6000fd5b505050507f1076343a2d116e6b4937d6f4987a4dc2df059fcedd58ffd950bca6b443a0a188836006811115613a6b57fe5b836006811115613a7757fe5b6040805163ffffffff938416815291831660208301529184168183015290519081900360600190a150505050565b600080613abd6000601254600101600060155461374b565b90508015613aff576001836006811115613ad357fe5b1415613ae25760029150613b2d565b6002836006811115613af057fe5b1415613aff5760039150613b2d565b6003836006811115613b0d57fe5b148015613b1b5750601e5415155b15613b295760049150613b2d565b8291505b50919050565b600082820183811015613b4257fe5b9392505050565b600080851515613b5857613dbc565b8591508415613d1857604080517ff6ff810e000000000000000000000000000000000000000000000000000000008152306004820152600160a060020a03898116602483015282519087169263f6ff810e92604480820193918290030181600087803b158015613bc757600080fd5b505af1158015613bdb573d6000803e3d6000fd5b505050506040513d6040811015613bf157600080fd5b5051905081811115613bff57fe5b6000811115613d185782600160a060020a031663095ea7b385836040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015613c6b57600080fd5b505af1158015613c7f573d6000803e3d6000fd5b505050506040513d6020811015613c9557600080fd5b50511515613c9f57fe5b83600160a060020a031663c033a490886040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050600060405180830381600087803b158015613cfa57600080fd5b505af1158015613d0e573d6000803e3d6000fd5b5050505080820391505b6000821115613dbc576040805160e160020a635f22feb1028152600160a060020a038981166004830152602482018590526060604483015260006064830181905292519086169263be45fd629260a480820193602093909283900390910190829087803b158015613d8857600080fd5b505af1158015613d9c573d6000803e3d6000fd5b505050506040513d6020811015613db257600080fd5b50511515613dbc57fe5b50505050505050565b6000828202831580613de15750828482811515613dde57fe5b04145b1515613b4257fe5b6000613b428383670de0b6b3a7640000612c83565b60006003826006811115613e0e57fe5b148015613e2e5750600f54601d54607060020a900466ffffffffffffff16105b15613b2d5750600661176e565b6003816006811115613e4957fe5b1415613e5757613e57613eab565b6004816006811115613e6557fe5b1415613e7357613e7361463c565b6006816006811115613e8157fe5b1415613e8f57613e8f614aba565b6005816006811115613e9d57fe5b14156123ab576123ab614d47565b6000806000806000806000806000601860009054906101000a9004600160a060020a0316600160a060020a031663c0c795676040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613f0c57600080fd5b505af1158015613f20573d6000803e3d6000fd5b505050506040513d6020811015613f3657600080fd5b5051604080517f436cdb030000000000000000000000000000000000000000000000000000000081529051919a50600160a060020a038b169163436cdb03916004808201926020929091908290030181600087803b158015613f9757600080fd5b505af1158015613fab573d6000803e3d6000fd5b505050506040513d6020811015613fc157600080fd5b5051601054601d54919950607060020a90910466ffffffffffffff16141561405b5788600160a060020a0316630216e3046040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561402157600080fd5b505af1158015614035573d6000803e3d6000fd5b505050506040513d602081101561404b57600080fd5b5051601054810397509550614136565b601a54601d54604080517f9ae0f890000000000000000000000000000000000000000000000000000000008152607060020a90920466ffffffffffffff16600483015251600160a060020a0390921691639ae0f890916024808201926020929091908290030181600087803b1580156140d357600080fd5b505af11580156140e7573d6000803e3d6000fd5b505050506040513d60208110156140fd57600080fd5b5051601d549097508890607060020a900466ffffffffffffff16880181151561412257fe5b069450600085111561413657848803870196505b601d54607060020a900466ffffffffffffff1687019350878481151561415857fe5b6020805467ffffffffffffffff8b811668010000000000000000026fffffffffffffffff000000000000000019959094041667ffffffffffffffff199091161792909216178155601a54601f54604080517f0cbab4f70000000000000000000000000000000000000000000000000000000081526001607060020a03909216600483015251600160a060020a0390921692630cbab4f7926024808401938290030181600087803b15801561420b57600080fd5b505af115801561421f573d6000803e3d6000fd5b505050506040513d602081101561423557600080fd5b5051602080547fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff16608060020a6001607060020a0393841602178155601a54601f54604080517f0cbab4f7000000000000000000000000000000000000000000000000000000008152607060020a90920490941660048201529251600160a060020a0390911692630cbab4f79260248083019391928290030181600087803b1580156142e057600080fd5b505af11580156142f4573d6000803e3d6000fd5b505050506040513d602081101561430a57600080fd5b5051602180546dffffffffffffffffffffffffffff199081166001607060020a03938416179182905560208054601f80547bffffffffffffffffffffffffffff000000000000000000000000000019948116608060020a8404881691881691909103871617938416948616607060020a9485900487160390951690920292909217909255604080517ff5ee5904000000000000000000000000000000000000000000000000000000008152905167ffffffffffffffff90931692600160a060020a038d169263f5ee590492600480820193918290030181600087803b1580156143f257600080fd5b505af1158015614406573d6000803e3d6000fd5b505050506040513d602081101561441c57600080fd5b5051601f5491029350614440908490607060020a90046001607060020a03166152b3565b600a546017546040805160e160020a635f22feb1028152600160a060020a0392831660048201526001607060020a0385166024820152606060448201526000606482018190529151949650919092169263be45fd629260a480840193602093929083900390910190829087803b1580156144b957600080fd5b505af11580156144cd573d6000803e3d6000fd5b505050506040513d60208110156144e357600080fd5b505115156144ed57fe5b601f80546001607060020a03607060020a8083048216869003909116027bffffffffffffffffffffffffffff00000000000000000000000000001990911617905560208054604080517fc46ca0d8000000000000000000000000000000000000000000000000000000008152905167ffffffffffffffff90921692600160a060020a038d169263c46ca0d89260048082019392918290030181600087803b15801561459757600080fd5b505af11580156145ab573d6000803e3d6000fd5b505050506040513d60208110156145c157600080fd5b50516017546016546020805460408051600160a060020a039586168152939094169183019190915267ffffffffffffffff1681830152929091026060830181905290519092507fa4cfdddd325124fc2d9f4fa352e25b682cbbb596be1c4a9523c423f0c308bef99181900360800190a1505050505050505050565b600854604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290516000928392600160a060020a03909116916370a082319160248082019260209290919082900301818787803b1580156146a657600080fd5b505af11580156146ba573d6000803e3d6000fd5b505050506040513d60208110156146d057600080fd5b505191506146dd8261371b565b50600854601b546040805160e160020a635f22feb1028152600160a060020a03928316600482015260248101859052606060448201526000606482018190529151949550919092169263be45fd629260a480840193602093929083900390910190829087803b15801561474f57600080fd5b505af1158015614763573d6000803e3d6000fd5b505050506040513d602081101561477957600080fd5b5051151561478357fe5b601f5460006001607060020a03909116111561484657600954601654601f546040805160e160020a635f22feb1028152600160a060020a0393841660048201526001607060020a039092166024830152606060448301526000606483018190529051929093169263be45fd629260a4808401936020939083900390910190829087803b15801561481257600080fd5b505af1158015614826573d6000803e3d6000fd5b505050506040513d602081101561483c57600080fd5b5051151561484657fe5b601f546000607060020a9091046001607060020a0316111561491557600a54601654601f546040805160e160020a635f22feb1028152600160a060020a039384166004820152607060020a9092046001607060020a03166024830152606060448301526000606483018190529051929093169263be45fd629260a4808401936020939083900390910190829087803b1580156148e157600080fd5b505af11580156148f5573d6000803e3d6000fd5b505050506040513d602081101561490b57600080fd5b5051151561491557fe5b600d54602054604080517fa5820daa0000000000000000000000000000000000000000000000000000000081526801000000000000000090920467ffffffffffffffff16600483015251600160a060020a039092169163a5820daa9160248082019260009290919082900301818387803b15801561499257600080fd5b505af11580156149a6573d6000803e3d6000fd5b5050601b5460408051600160a060020a03909216825260208201869052818101859052517fe540df3afae3936e26f26bbdb7d7eba37fb1c41ccc19d2da9c8191869e85dc3a9350908190036060019150a1601654600954601f5460408051600160a060020a0394851681529290931660208301526001607060020a03168183015290517feb5dfd984307753c74358459c54dbedb2525db2c9b1c4c8eee9db382b1e391c59181900360600190a1601654600a54601f5460408051600160a060020a039485168152939092166020840152607060020a90046001607060020a031682820152517feb5dfd984307753c74358459c54dbedb2525db2c9b1c4c8eee9db382b1e391c5916060908290030190a15050565b600854604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290516000928392600160a060020a03909116916370a082319160248082019260209290919082900301818787803b158015614b2457600080fd5b505af1158015614b38573d6000803e3d6000fd5b505050506040513d6020811015614b4e57600080fd5b5051600d54604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051929450600160a060020a03909116916370a08231916024808201926020929091908290030181600087803b158015614bb957600080fd5b505af1158015614bcd573d6000803e3d6000fd5b505050506040513d6020811015614be357600080fd5b505190506000821115614c6f57600854604080517f42966c68000000000000000000000000000000000000000000000000000000008152600481018590529051600160a060020a03909216916342966c689160248082019260009290919082900301818387803b158015614c5657600080fd5b505af1158015614c6a573d6000803e3d6000fd5b505050505b6000811115614cf757600d54604080517f67fbd289000000000000000000000000000000000000000000000000000000008152600481018490529051600160a060020a03909216916367fbd2899160248082019260009290919082900301818387803b158015614cde57600080fd5b505af1158015614cf2573d6000803e3d6000fd5b505050505b600d5460408051600160a060020a03909216825260208201839052818101849052517f8d5b2366bddfdb8f9f6e9b9f609d5bcc78a8633d87dae106f4e9888187275ed19181900360600190a15050565b6000806060600760009054906101000a9004600160a060020a0316600160a060020a031663494115576040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015614d9f57600080fd5b505af1158015614db3573d6000803e3d6000fd5b505050506040513d6020811015614dc957600080fd5b50519250600160a060020a0383161515614ddf57fe5b600760009054906101000a9004600160a060020a0316600160a060020a031663f40a80ea6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015614e3257600080fd5b505af1158015614e46573d6000803e3d6000fd5b505050506040513d6020811015614e5c57600080fd5b50519150600160a060020a0382161515614e7257fe5b5060085460408051600160a060020a03909216606060020a02602080840191909152815160148185030181526034909301909152546000608060020a9091046001607060020a03161115614fcd576009546020805460405160e160020a635f22feb1028152600160a060020a0387811660048301908152608060020a9093046001607060020a031660248301819052606060448401908152875160648501528751929096169563be45fd62958a959294899460849091019185019080838360005b83811015614f4b578181015183820152602001614f33565b50505050905090810190601f168015614f785780820380516001836020036101000a031916815260200191505b50945050505050602060405180830381600087803b158015614f9957600080fd5b505af1158015614fad573d6000803e3d6000fd5b505050506040513d6020811015614fc357600080fd5b50511515614fcd57fe5b60215460006001607060020a0390911611156150ec57600a5460215460405160e160020a635f22feb1028152600160a060020a03868116600483019081526001607060020a0390931660248301819052606060448401908152865160648501528651929095169463be45fd629489949293889391929160840190602085019080838360005b8381101561506a578181015183820152602001615052565b50505050905090810190601f1680156150975780820380516001836020036101000a031916815260200191505b50945050505050602060405180830381600087803b1580156150b857600080fd5b505af11580156150cc573d6000803e3d6000fd5b505050506040513d60208110156150e257600080fd5b505115156150ec57fe5b600d54602054604080517f158a4988000000000000000000000000000000000000000000000000000000008152600160a060020a0386811660048301526801000000000000000090930467ffffffffffffffff1660248201529051919092169163158a498891604480830192600092919082900301818387803b15801561517257600080fd5b505af1158015615186573d6000803e3d6000fd5b50506009546020805460408051600160a060020a039485168152938916928401929092526001607060020a03608060020a9091041682820152517f6e6fd9b5ea7bbd5055657dcdd81818e67183bd59bcfb929db8da7a130b9f37c89350908190036060019150a1600a5460215460408051600160a060020a03938416815292861660208401526001607060020a0390911682820152517f6e6fd9b5ea7bbd5055657dcdd81818e67183bd59bcfb929db8da7a130b9f37c89181900360600190a1600d546020805460408051600160a060020a0394851681529386169284019290925267ffffffffffffffff680100000000000000009091041682820152517f3d5f381e0481c18a6e87775195ba5c8e32f97f48426ad47b7eca0eb6828bcbe79181900360600190a1505050565b60008183106152c25781613b42565b5090919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061530a57805160ff1916838001178555615337565b82800160010185558215615337579182015b8281111561533757825182559160200191906001019061531c565b50610b80929150615381565b60e0604051908101604052806007906020820280388339509192915050565b6040805160608181018352600080835260208301529181019190915290565b6109a691905b80821115610b8057600081556001016153875600a165627a7a7230582056a74eca5a01dd4fa58a7c81c8c45ebe3df29b0fa05f3d1740f03e897744add50029
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000082fb5126506b6c315fa4a7ae3d4cb8a46a1aae67000000000000000000000000f3f35c09a8e9de32a8d60451bd80e5381cd8f52d000000000000000000000000b8a93fdc69df45c59302fe867877786a5e05be050000000000000000000000007ddf45e48fd60b736f2c45252c14f7da7c88310d000000000000000000000000535bfaeb50580f674bd2e076d6073adf28a46fa8
-----Decoded View---------------
Arg [0] : universe (address): 0x82FB5126506b6c315FA4a7aE3d4CB8A46A1AAe67
Arg [1] : nominee (address): 0xf3f35C09A8e9dE32A8D60451BD80E5381Cd8F52d
Arg [2] : companyLegalRep (address): 0xB8a93FDC69Df45c59302FE867877786A5e05bE05
Arg [3] : etoTerms (address): 0x7dDF45E48Fd60b736f2C45252c14F7dA7c88310d
Arg [4] : equityToken (address): 0x535BFaeB50580F674bD2e076D6073aDF28A46fA8
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000082fb5126506b6c315fa4a7ae3d4cb8a46a1aae67
Arg [1] : 000000000000000000000000f3f35c09a8e9de32a8d60451bd80e5381cd8f52d
Arg [2] : 000000000000000000000000b8a93fdc69df45c59302fe867877786a5e05be05
Arg [3] : 0000000000000000000000007ddf45e48fd60b736f2c45252c14f7da7c88310d
Arg [4] : 000000000000000000000000535bfaeb50580f674bd2e076d6073adf28a46fa8
Deployed Bytecode Sourcemap
243248:40768:0:-;;;;;;;;;-1:-1:-1;;;243248:40768:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;234980:270;;8:9:-1;5:2;;;30:1;27;20:12;5:2;234980:270:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;234222:163;;8:9:-1;5:2;;;30:1;27;20:12;5:2;234222:163:0;;;;;;;;;;;;;;;;;;;;;;253082:359;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;253082:359:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;253082:359:0;;-1:-1:-1;253082:359:0;;-1:-1:-1;;;;;;;253082:359:0;;;258649:293;;8:9:-1;5:2;;;30:1;27;20:12;5:2;258649:293:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;235258:274;;8:9:-1;5:2;;;30:1;27;20:12;5:2;235258:274:0;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;235258:274:0;;;;;;;;;;;;;;;;258082:86;;8:9:-1;5:2;;;30:1;27;20:12;5:2;258082:86:0;;;;;;;;-1:-1:-1;;;;;258082:86:0;;;;;;;;;;;;;;257876:90;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257876:90:0;;;;235540:124;;8:9:-1;5:2;;;30:1;27;20:12;5:2;235540:124:0;;;;246186:38;;8:9:-1;5:2;;;30:1;27;20:12;5:2;246186:38:0;;;;257127:741;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257127:741:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;256343:175;;8:9:-1;5:2;;;30:1;27;20:12;5:2;256343:175:0;;;;4174:656;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4174:656:0;-1:-1:-1;;;;;4174:656:0;;;;;;;;;;256526:160;;8:9:-1;5:2;;;30:1;27;20:12;5:2;256526:160:0;;;;246009:48;;8:9:-1;5:2;;;30:1;27;20:12;5:2;246009:48:0;;;;251782:1292;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;251782:1292:0;-1:-1:-1;;;;;251782:1292:0;;;;;;;;;;;;256694:174;;8:9:-1;5:2;;;30:1;27;20:12;5:2;256694:174:0;;;;256959:160;;8:9:-1;5:2;;;30:1;27;20:12;5:2;256959:160:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;256959:160:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16984:174;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16984:174:0;-1:-1:-1;;;;;16984:174:0;;;;;;;;;;;;;;;;;;;;;17166:137;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17166:137:0;;;;262558:169;;8:9:-1;5:2;;;30:1;27;20:12;5:2;262558:169:0;;;;;;;;;;;;;;;;;;;;;;;;;;;258299:342;;8:9:-1;5:2;;;30:1;27;20:12;5:2;258299:342:0;;;;;;;;-1:-1:-1;;;;;258299:342:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;262160:341;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;262160:341:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;262160:341:0;;-1:-1:-1;262160:341:0;;-1:-1:-1;;;;;;;262160:341:0;16423:553;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;16423:553:0;;;;;;;;;;-1:-1:-1;;;;;16423:553:0;-1:-1:-1;;;;;16423:553:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;16423:553:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;234740:139;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;234740:139:0;;;;;;;233987:194;;8:9:-1;5:2;;;30:1;27;20:12;5:2;233987:194:0;;;;260679:1175;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;260679:1175:0;-1:-1:-1;;;;;260679:1175:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;234425:132;;8:9:-1;5:2;;;30:1;27;20:12;5:2;234425:132:0;;;;254385:1898;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;254385:1898:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;254385:1898:0;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;254385:1898:0;;-1:-1:-1;254385:1898:0;;-1:-1:-1;;;;;;;254385:1898:0;234616:116;;8:9:-1;5:2;;;30:1;27;20:12;5:2;234616:116:0;;;;257974:100;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257974:100:0;;;;15811:604;;8:9:-1;5:2;;;30:1;27;20:12;5:2;15811:604:0;;;;258950:1721;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;258950:1721:0;-1:-1:-1;;;;;258950:1721:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15646:157;;8:9:-1;5:2;;;30:1;27;20:12;5:2;15646:157:0;;;;15199:439;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;15199:439:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;15199:439:0;;-1:-1:-1;15199:439:0;;-1:-1:-1;;;;;;;15199:439:0;4838:135;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4838:135:0;;;;233806:93;;8:9:-1;5:2;;;30:1;27;20:12;5:2;233806:93:0;;;;253449:674;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;253449:674:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;253449:674:0;;-1:-1:-1;253449:674:0;;-1:-1:-1;;;;;;;253449:674:0;258176:115;;8:9:-1;5:2;;;30:1;27;20:12;5:2;258176:115:0;;;;234980:270;235056:8;235199:19;:17;:19::i;:::-;-1:-1:-1;235236:6:0;;-1:-1:-1;;;235236:6:0;;;;234980:270;;:::o;234222:163::-;234293:4;234333:14;234323:6;;-1:-1:-1;;;234323:6:0;;;;:24;;;;;;;;;:53;;;-1:-1:-1;234361:15:0;234351:25;:6;;-1:-1:-1;;;234351:6:0;;;;:25;;;;;;;;;234323:53;234315:62;;234222:163;:::o;253082:359::-;233070:19;:17;:19::i;:::-;253226:16;;233282:6;;-1:-1:-1;;;233282:6:0;;;;:15;;;;;;;;;233274:24;;;;;;248005:28;;-1:-1:-1;;;;;248005:28:0;247991:10;:42;247983:51;;;;;;253281:60;;;;:29;;:60;;;;;:::i;:::-;-1:-1:-1;253395:7:0;;253357:76;;;253383:10;253357:76;;;-1:-1:-1;;;;;253395:7:0;;;253357:76;;;;;;;;;;;;;;;;;;;;;;;;;;;253383:10;253395:7;;253404:28;;253357:76;;;;;;;;;;;;;;253395:7;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;253357:76:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;233134:1;233193:19;:17;:19::i;:::-;253082:359;:::o;258649:293::-;258881:18;;-1:-1:-1;;;;;258881:18:0;;;-1:-1:-1;;;258901:15:0;;;;;-1:-1:-1;;;258918:15:0;;;;;258649:293::o;235258:274::-;235335:19;;:::i;:::-;235410:10;235406:119;26750:1;235425:2;:19;235406:119;;;235484:29;235509:2;235500:12;;;;;;;;235484:15;:29::i;:::-;235469:8;235478:2;235469:12;;;;;;;;;;:44;235451:1;235445:7;235406:119;;;235258:274;;:::o;258082:86::-;258153:7;;-1:-1:-1;;;;;258153:7:0;258082:86;:::o;257876:90::-;257949:9;;-1:-1:-1;;;;;257949:9:0;257876:90;:::o;235540:124::-;235637:19;;-1:-1:-1;;;;;235637:19:0;235540:124;:::o;246186:38::-;;;-1:-1:-1;;;;;246186:38:0;;:::o;257127:741::-;257224:17;257243:27;257285:33;257320:37;257372:32;257406:22;257430:26;257471:25;257546:10;;;;;;;;;;;257571:9;;;;;;;;;-1:-1:-1;;;;;257571:9:0;-1:-1:-1;;;;;257571:21:0;;:23;;;;;-1:-1:-1;;;257571:23:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257571:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;257571:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;257571:23:0;:50;;;;;;;;-1:-1:-1;;;;;257571:48:0;;;;;;:50;;;;;:23;;:50;;;;;;;;;:48;:50;;;5:2:-1;;;;30:1;27;20:12;5:2;257571:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;257571:50:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;257571:50:0;;257558:10;257636:26;;257753:19;;257558:10;;;;:63;;;;-1:-1:-1;;;;;257636:26:0;;;;-1:-1:-1;;;257664:30:0;;;;;;257709:25;;;;;;;-1:-1:-1;;;257736:15:0;;;;;;257753:19;;257787:15;:62;;257818:18;;257838:10;;257809:40;;-1:-1:-1;;;;;257818:18:0;;257838:10;;257809:8;:40::i;:::-;257787:62;;;257805:1;257787:62;257524:336;;;;;-1:-1:-1;257524:336:0;;-1:-1:-1;;;;;;257524:336:0;;;;-1:-1:-1;257524:336:0;;;;-1:-1:-1;257524:336:0;;;-1:-1:-1;257524:336:0;;;;-1:-1:-1;257524:336:0;;;;;-1:-1:-1;257524:336:0;-1:-1:-1;257127:741:0;;;;;;;;:::o;256343:175::-;233070:19;:17;:19::i;:::-;256429:14;256445:15;256429:14;233399:6;;-1:-1:-1;;;233399:6:0;;;;:16;;;;;;;;;:36;;;;233429:6;233419:16;;;;;;;;:6;;-1:-1:-1;;;233419:6:0;;;;:16;;;;;;;;;233399:36;233391:45;;;;;;;;256480:30;256499:10;256480:18;:30::i;:::-;233134:1;;233193:19;:17;:19::i;:::-;256343:175::o;4174:656::-;4651:23;3733:13;;:54;;;;;;3755:10;3733:54;;;;2546:66;3733:54;;;;;;3773:4;3733:54;;;;-1:-1:-1;;3779:7:0;;;3733:54;;;;;;2546:66;;-1:-1:-1;;;;;3733:13:0;;:21;;:54;;;;;;;;;;;;;;;4651:23;3733:13;:54;;;5:2:-1;;;;30:1;27;20:12;5:2;3733:54:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3733:54:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;3733:54:0;3725:63;;;;;;;;4488:77;;;;;;-1:-1:-1;;;;;4488:77:0;;;;;;;2546:66;4488:77;;;;4551:4;4488:77;;;;-1:-1:-1;4557:7:0;;-1:-1:-1;;4557:7:0;4488:77;;;;;;:17;;;;;;:77;;;;;;;;;;;;;;;;;;:17;:77;;;5:2:-1;;;;30:1;27;20:12;5:2;4488:77:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4488:77:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;4488:77:0;4480:86;;;;;;;;4677:13;;;-1:-1:-1;;;;;4701:25:0;;;-1:-1:-1;;4701:25:0;;;;;;;4766:56;;;4789:10;4766:56;;4677:13;;;;4766:56;;;;;;;;;;;;;;;4677:13;;-1:-1:-1;4766:56:0;;;;;;;;;;4174:656;;;;:::o;256526:160::-;233070:19;:17;:19::i;:::-;256612:15;;233282:6;;-1:-1:-1;;;233282:6:0;;;;:15;;;;;;;;;233274:24;;;;;;256647:31;256667:10;256647:19;:31::i;:::-;233134:1;233193:19;:17;:19::i;246009:48::-;;;-1:-1:-1;;;;;246009:48:0;;:::o;251782:1292::-;248005:28;;252488:15;;-1:-1:-1;;;;;248005:28:0;247991:10;:42;247983:51;;;;;;248220:1;248200:17;:15;:17::i;:::-;:21;248192:30;;;;;;233070:19;:17;:19::i;:::-;252018:14;;233282:6;;-1:-1:-1;;;233282:6:0;;;;:15;;;;;;;;;233274:24;;;;;;252070:9;;-1:-1:-1;;;;;252058:21:0;;;252070:9;;252058:21;252050:30;;;;;;252114:12;;-1:-1:-1;;;;;252099:27:0;;;252114:12;;252099:27;252091:36;;;;;;252157:10;252145:22;;252138:30;;;;252270:15;252258:9;:27;:115;;;;;252319:21;;;;;;;;;-1:-1:-1;;;;;252319:21:0;-1:-1:-1;;;;;252319:52:0;;:54;;;;;-1:-1:-1;;;252319:54:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;252319:54:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;252319:54:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;252319:54:0;252301:15;252289:27;;:84;252258:115;252236:176;;;;;;;-1:-1:-1;;;;;252236:176:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;252506:35;252522:18;252506:15;:35::i;:::-;252488:53;-1:-1:-1;252670:12:0;;;:100;;;252715:21;;;;;;;;;-1:-1:-1;;;;;252715:21:0;-1:-1:-1;;;;;252715:52:0;;:54;;;;;-1:-1:-1;;;252715:54:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;252715:54:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;252715:54:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;252715:54:0;252697:15;252687:25;;:82;252670:100;252648:161;;;;;;;-1:-1:-1;;;;;252648:161:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;252820:34;252843:9;252820:15;:34::i;:::-;252936:64;;;252948:10;252936:64;;-1:-1:-1;;;;;252936:64:0;;;;;;;;;;;;;;;;;;;;;;;;253016:50;;;253035:10;253016:50;;;;;;;;;;;;;;;;;;;;;;;;;233134:1;233193:19;:17;:19::i;:::-;251782:1292;;;;:::o;256694:174::-;233070:19;:17;:19::i;:::-;256780:15;;233282:6;;-1:-1:-1;;;233282:6:0;;;;:15;;;;;;;;;233274:24;;;;;256959:160;257082:29;257075:36;;;;;;;;-1:-1:-1;;257075:36:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;257051:6;;257075:36;;257082:29;;257075:36;;257082:29;257075:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;256959:160;:::o;16984:174::-;-1:-1:-1;;;;;17127:23:0;17087:15;17127:23;;;:12;:23;;;;;;;16984:174::o;17166:137::-;17277:11;:18;17166:137;:::o;262558:169::-;262649:66;262717:1;262558:169;;:::o;258299:342::-;258539:23;;258584:8;;258619:14;;-1:-1:-1;;;;;258539:23:0;;;;258584:8;;;;258619:14;;258299:342::o;262160:341::-;262263:23;;;262230:15;;233282:6;;-1:-1:-1;;;233282:6:0;;;;:15;;;;;;;;;233274:24;;;;;;262303:8;;;;;;;;;-1:-1:-1;;;;;262303:8:0;-1:-1:-1;;;;;262303:21:0;;:23;;;;;-1:-1:-1;;;262303:23:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;262303:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;262303:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;262303:23:0;;-1:-1:-1;262355:1:0;;-1:-1:-1;262338:156:0;262362:6;:13;262358:1;:17;262338:156;;;262416:6;262423:1;262416:9;;;;;;;;;;;;;;;;;;;262464:7;;262440:42;;;;;;-1:-1:-1;;;;;262440:42:0;;;;;;;262464:7;;;262440:42;;;;262473:8;262440:42;;;;;;262416:9;;-1:-1:-1;262440:16:0;;;;;;:42;;;;;262464:7;;262440:42;;;;;;;;262464:7;262440:16;:42;;;5:2:-1;;;;30:1;27;20:12;5:2;262440:42:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;262440:42:0;;;;262382:1;262377:6;;;;262338:156;;;262160:341;;;;;:::o;16423:553::-;16545:35;16595:28;16638:19;16672:13;16713:33;16749:11;16761:14;16749:27;;;;;;;;;;;;;;;;;;;;16713:63;;16809:9;:37;;;;;;;;;;-1:-1:-1;;;;;16809:37:0;16861:9;:30;;;16906:9;:22;;16943:14;16787:181;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;16423:553;;;;;;:::o;234740:139::-;234821:7;234853:18;234869:1;234853:15;:18::i;:::-;234846:25;234740:139;-1:-1:-1;;234740:139:0:o;233987:194::-;234060:4;234100:15;234090:6;;-1:-1:-1;;;234090:6:0;;;;:25;;;;;;;;;:54;;;-1:-1:-1;234129:15:0;234119:6;;-1:-1:-1;;;234119:6:0;;;;:25;;;;;;;;;234090:54;:82;;;-1:-1:-1;234158:14:0;234148:24;;260679:1175;-1:-1:-1;;;;;261180:18:0;;;260787:20;261180:18;;;:8;:18;;;;;;;;261285:19;;;261379:21;;;261424:9;;:53;;;;;-1:-1:-1;;;;;261379:21:0;;;261424:53;;;;;;;;261285:19;;;;-1:-1:-1;;;261331:20:0;;;;;;;261379:21;;260787:20;;;;;;;;;;;;261424:9;;;;;:30;;:53;;;;;261180:18;261424:53;;;;;260787:20;261424:9;:53;;;5:2:-1;;;;30:1;27;20:12;5:2;261424:53:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;261424:53:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;261424:53:0;;-1:-1:-1;261518:1:0;261501:18;;:54;;261554:1;261501:54;;;261537:14;261522:12;:29;;;;;;;;261501:54;261488:67;;261592:1;261576:13;:17;:71;;261646:1;261576:71;;;261596:47;261607:12;261621:6;261629:13;261596:10;:47::i;:::-;261670:16;;;;261713:20;;;;;260679:1175;;;;-1:-1:-1;260679:1175:0;;;;;;261566:81;-1:-1:-1;;;;;;;;261670:16:0;;;;;;;261713:20;;;;-1:-1:-1;261764:27:0;;;;;;;-1:-1:-1;261822:24:0;;;;;;;;-1:-1:-1;260679:1175:0;-1:-1:-1;;260679:1175:0:o;234425:132::-;234495:4;234534:15;234524:25;;254385:1898;254722:16;255064:20;255360:22;255397:31;255443:19;255557:24;233070:19;:17;:19::i;:::-;254519:18;254539:15;254519:18;233399:6;;-1:-1:-1;;;233399:6:0;;;;:16;;;;;;;;;:36;;;;233429:6;233419:16;;;;;;;;:6;;-1:-1:-1;;;233419:6:0;;;;:16;;;;;;;;;233399:36;233391:45;;;;;;;;254641:11;;-1:-1:-1;;;;;254641:11:0;254619:10;:34;;:71;;-1:-1:-1;254679:10:0;;-1:-1:-1;;;;;254679:10:0;254657;:33;254619:71;254611:100;;;;;;;-1:-1:-1;;;;;254611:100:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;254851:10;;254741:6;;-1:-1:-1;;;;;;254833:29:0;;;254851:10;;254833:29;;:61;;-1:-1:-1;254884:9:0;;-1:-1:-1;;;;;254866:28:0;;;254884:9;;254866:28;254833:61;254829:171;;;254969:19;254983:4;254969:13;:19::i;:::-;254958:30;;254829:171;255109:10;;-1:-1:-1;;;;;255109:10:0;255087;:33;:70;;255132:25;255150:6;255132:17;:25::i;:::-;255087:70;;;255123:6;255087:70;255064:93;;255242:33;255266:8;255242:23;:33::i;:::-;255466:43;255478:8;255488:6;255496:12;255466:11;:43::i;:::-;255341:168;;;;;;;;;;255584:178;255619:8;255642:6;255663:12;255690:11;255716:14;255745:6;255584:20;:178::i;:::-;255557:205;;255811:181;255847:14;255876:23;255914:12;255941:6;255962:19;255811:21;:181::i;:::-;256226:12;;256048:227;;;-1:-1:-1;;;;;256048:227:0;;;;;256124:10;256048:227;;;;;;;;;;;;;;;;;;;;;;256226:12;;;256048:227;;;;;;;;;;;;;;;;;;;;;;;;;233134:1;;233193:19;:17;:19::i;:::-;254385:1898;;;;;;;;;:::o;234616:116::-;234718:6;;-1:-1:-1;;;234718:6:0;;;;;234616:116::o;257974:100::-;258054:12;;-1:-1:-1;;;;;258054:12:0;257974:100;:::o;15811:604::-;15914:35;15964:28;16007:19;16041:13;16124:12;16172:33;16111:1;16090:11;:18;;;;:22;16082:31;;;;;;;;16139:11;:18;;-1:-1:-1;;16139:22:0;;;-1:-1:-1;16139:22:0;;16208:17;;;;;;;;;;;;;;;;16172:53;;16258:9;:37;;;;;;;;;;-1:-1:-1;;;;;16258:37:0;16310:9;:30;;;16355:9;:22;;16392:4;16236:171;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15811:604;;;;;;:::o;258950:1721::-;259213:18;259246:15;259276:24;259315;259354:22;259391:21;259427:19;259461:26;259519:31;259582:32;233070:19;:17;:19::i;:::-;259553:8;:18;259562:8;-1:-1:-1;;;;;259553:18:0;-1:-1:-1;;;;;259553:18:0;;;;;;;;;;;;259519:52;;259822:9;;;;;;;;;-1:-1:-1;;;;;259822:9:0;-1:-1:-1;;;;;259822:31:0;;259868:8;259891:18;;;;;;;;;-1:-1:-1;;;;;259891:18:0;259924:6;:19;;;;;;;;;;-1:-1:-1;;;;;259924:19:0;259958:30;260100:18;260089:29;;;;;;;;:7;:5;:7::i;:::-;:29;;;;;;;;259822:307;;;-1:-1:-1;;;259822:307:0;;;;;;-1:-1:-1;;;;;259822:307:0;;;;;;;-1:-1:-1;;;;;259822:307:0;;;;;;;-1:-1:-1;;;;;259822:307:0;;;;;;;;;;;;;;260089:29;259822:307;;;;;;;;;;;;;;;;;;;-1:-1:-1;259822:307:0;;;;5:2:-1;;;;30:1;27;20:12;5:2;259822:307:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;259822:307:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;259822:307:0;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;259822:307:0;;-1:-1:-1;259822:307:0;-1:-1:-1;259822:307:0;-1:-1:-1;259822:307:0;;-1:-1:-1;259822:307:0;-1:-1:-1;259822:307:0;;260156:31;;;260173:14;260156:31;260140:47;;260203:14;260202:15;260198:148;;;260282:7;;:51;;;;;;;;;;;;;;260253:81;;-1:-1:-1;;;;;260282:7:0;;:19;;:51;;;;;;;;;;;;;;:7;;:51;;;5:2:-1;;;;30:1;27;20:12;5:2;260282:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;260282:51:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;260282:51:0;260253:28;:81::i;:::-;260234:100;-1:-1:-1;;260198:148:0;260420:118;260445:18;260434:7;:5;:7::i;:::-;:29;;;;;;;;;260465:14;260481:24;260507:30;260420:13;:118::i;:::-;260403:135;;260633:30;260612:51;;233193:19;:17;:19::i;:::-;258950:1721;;;;;;;;;;;;;:::o;15646:157::-;15774:21;;-1:-1:-1;;;;;15774:21:0;15646:157;:::o;15199:439::-;15321:32;;:::i;:::-;15293:10;14712:30;14722:19;14712:9;:30::i;:::-;14704:39;;;;;;;;15356:175;;;;;;;;15416:10;15356:175;;15463:15;15356:175;;;;;;;;;;;;;15542:11;27:10:-1;;39:1;23:18;;45:23;;;-1:-1;15542:27:0;;;;;;;;;;;;;;;;-1:-1:-1;;15542:27:0;-1:-1:-1;;;;;15542:27:0;;;;;;;;;;;;;;;;;;;15356:175;;-1:-1:-1;23:18;;15356:175:0;;15542:27;;;;;;;;;;:::i;:::-;;;;;15585:45;15605:10;15617:12;15585:45;;;;-1:-1:-1;;;;;15585:45:0;-1:-1:-1;;;;;15585:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;15585:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15199:439;;;:::o;4838:135::-;4914:13;4952;-1:-1:-1;;;;;4952:13:0;4838:135;:::o;233806:93::-;233872:19;:17;:19::i;253449:674::-;253651:19;233070;:17;:19::i;:::-;253596:16;;233282:6;;-1:-1:-1;;;233282:6:0;;;;:15;;;;;;;;;233274:24;;;;;;248118:7;;-1:-1:-1;;;;;248118:7:0;248104:10;:21;248096:30;;;;;;253700:28;253683:46;;;;;;;;;;;;;;;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;253683:46:0;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;253683:46:0;;;253673:57;;;;;;;;;;;;;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;253673:57:0;;;;;;;;;;;;;;;;253651:79;;253811:11;253749:73;;;253776:29;253759:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;253759:47:0;;;253749:58;;;;;;;;;;;;;36:153:-1;66:2;58:11;;36:153;;176:10;;164:23;;-1:-1;;139:12;;;;98:2;89:12;;;;114;36:153;;;274:1;267:3;263:2;259:12;254:3;250:22;246:30;315:4;311:9;305:3;299:10;295:26;356:4;350:3;344:10;340:21;389:7;380;377:20;372:3;365:33;3:399;;;253749:58:0;;;;;;;;;;;;;;;;:73;;;;253741:97;;;;;;;-1:-1:-1;;;;;253741:97:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;253945:40;:54;;;254056:28;;254015:100;;;254044:10;254015:100;;;-1:-1:-1;;;;;254056:28:0;;;254015:100;;;;;;;;;;;;;;;;;;;;;;;;;;;254044:10;254056:28;;254086;;254015:100;;;;;;;;;;;;;;253945:54;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;254015:100:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;233134:1;233193:19;:17;:19::i;:::-;253449:674;;:::o;258176:115::-;258255:28;;-1:-1:-1;;;;;258255:28:0;258176:115;:::o;238183:1173::-;238328:25;:49;238429:9;;238328:49;;:54;238324:93;;;238399:7;;238324:93;-1:-1:-1;238441:15:0;238481:14;238471:6;;-1:-1:-1;;;238471:6:0;;;;:24;;;;;;;;;:68;;;;;238504:35;238520:18;238504:15;:35::i;:::-;238499:1;:40;;238471:68;238467:133;;;238556:32;238569:18;238556:12;:32::i;:::-;238624:18;238614:6;;-1:-1:-1;;;238614:6:0;;;;:28;;;;;;;;;:69;;;;;238651:32;238667:15;238651;:32::i;:::-;238646:1;:37;;238614:69;238610:131;;;238700:29;238713:15;238700:12;:29::i;:::-;238765:15;238755:6;;-1:-1:-1;;;238755:6:0;;;;:25;;;;;;;;;:67;;;;;238789:33;238805:16;238789:15;:33::i;:::-;238784:1;:38;;238755:67;238751:130;;;238839:30;238852:16;238839:12;:30::i;:::-;239070:16;239060:6;;-1:-1:-1;;;239060:6:0;;;;:26;;;;;;;;;:66;;;;;239095:31;239111:14;239095:15;:31::i;:::-;239090:1;:36;;239060:66;239056:128;;;239143:29;239156:15;239143:12;:29::i;:::-;239236:14;239226:6;;-1:-1:-1;;;239226:6:0;;;;:24;;;;;;;;;:65;;;;;239259:32;239275:15;239259;:32::i;:::-;239254:1;:37;;239226:65;239222:127;;;239308:29;239321:15;239308:12;:29::i;239446:298::-;239548:6;;239508:17;;239528:27;;-1:-1:-1;;;239548:6:0;;;;239528:19;:27::i;:::-;239508:47;;239580:8;239570:18;;;;;;;;:6;;-1:-1:-1;;;239570:6:0;;;;:18;;;;;;;;;239566:171;;239605:22;239618:8;239605:12;:22::i;:::-;239706:19;:17;:19::i;236469:1409::-;236560:7;;;;236640:1;:19;;;;;;;;;236636:60;;;236683:1;236676:8;;;;236636:60;236956:15;236951:1;:20;;;;;;;;;236947:119;;;237005:1;236995:11;;;;;;;;:6;;-1:-1:-1;;;236995:6:0;;;;:11;;;;;;;;;:59;;237053:1;236995:59;;;237042:6;;237009:25;;-1:-1:-1;;;237042:6:0;;;;237035:14;;;;;;;;237009:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;236995:59;236988:66;;;;;;236947:119;237196:6;;-1:-1:-1;;;237196:6:0;;;;237189:14;;;;;;;;237172:31;;237184:1;237179;237172:9;;;;;;;;:13;:31;;;;237168:111;;;237227:25;237265:1;237260;237253:9;;;;;;;;:13;237227:40;;;;;;;;;;237168:111;237382:6;;237349:25;;-1:-1:-1;;;237382:6:0;;;;237375:14;;;;;;;;237349:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;237319:71;;;;237708:6;;;;;;;;;;;237701:14;;;;;;;;237718:1;237701:18;237682:37;;;;237677:157;237739:1;237732:9;;;;;;;;237721:20;;:8;:20;237677:157;;;237793:19;:29;;237813:8;;237793:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;237770:52;;;;;237743:10;;237677:157;;;237851:19;237844:26;;236469:1409;;;;;;:::o;50479:144::-;50568:7;50614:1;50600:11;50604:1;50609;50614;50607:3;50600;:11::i;:::-;:15;;;;;;;;;50479:144;-1:-1:-1;;;50479:144:0:o;281664:820::-;-1:-1:-1;;;;;281777:18:0;;281743:31;281777:18;;;:8;:18;;;;;281810:27;;;;-1:-1:-1;;;281810:27:0;;;;281806:66;;;281854:7;;281806:66;281886:19;;-1:-1:-1;;;;;281886:19:0;:24;281882:63;;;281927:7;;281882:63;281955:27;;;:34;;-1:-1:-1;;;;;281955:34:0;;;;;;;;282006:20;;-1:-1:-1;282006:20:0;;;-1:-1:-1;;;;;282006:20:0;:24;282002:107;;;282047:7;;282076:20;;282047:50;;;;;;-1:-1:-1;;;;;282047:50:0;;;;;;;-1:-1:-1;;;282076:20:0;;;-1:-1:-1;;;;;282076:20:0;282047:50;;;;;;:7;;;;;:18;;:50;;;;;-1:-1:-1;;282047:50:0;;;;;;;-1:-1:-1;282047:7:0;:50;;;5:2:-1;;;;30:1;27;20:12;5:2;282047:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;282047:50:0;;;;282002:107;282123:21;;;;282147:1;-1:-1:-1;;;;;282123:21:0;;;:25;282119:120;;;282165:12;;;282205:21;;;282165:62;;;;;;-1:-1:-1;;;;;282165:62:0;;;;;;;-1:-1:-1;;;;;282205:21:0;;;282165:62;;;;;;:12;;;;;:29;;:62;;;;;:12;;:62;;;;;;;:12;;:62;;;5:2:-1;;;;30:1;27;20:12;5:2;282165:62:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;282165:62:0;;;;282119:120;282253:24;;;;;;;;;282249:127;;;282294:10;;:28;;;;;;-1:-1:-1;;;;;282294:28:0;;;;;;;;;:10;;;;;:18;;:28;;;;;:10;;:28;;;;;;;:10;;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;282294:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;282337:9:0;;:27;;;;;;-1:-1:-1;;;;;282337:27:0;;;;;;;;;:9;;;;;-1:-1:-1;282337:17:0;;-1:-1:-1;282337:27:0;;;;;:9;;:27;;;;;;;:9;;:27;;;5:2:-1;;;;30:1;27;20:12;5:2;282337:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;282337:27:0;;;;282249:127;282418:12;;;282432:21;;;282455:20;;282391:85;;;-1:-1:-1;;;;;282432:21:0;;;282391:85;;-1:-1:-1;;;282455:20:0;;;;;;282391:85;;;;;;-1:-1:-1;;;;;282418:12:0;;;;282391:85;;;;;;;;;;;281664:820;;:::o;282492:701::-;-1:-1:-1;;;;;282606:18:0;;282572:31;282606:18;;;:8;:18;;;;;282639:27;;;;-1:-1:-1;;;282639:27:0;;;;282635:66;;;282683:7;;282635:66;282715:19;;-1:-1:-1;;;;;282715:19:0;:24;282711:63;;;282756:7;;282711:63;282784:27;;;:34;;-1:-1:-1;;282784:34:0;-1:-1:-1;;;282784:34:0;;;;;;;282814:4;282857:16;;;282901:10;;282913:11;;282829:96;;282847:8;;-1:-1:-1;;;;;282857:16:0;;;;;;;282875:24;;;282784:34;282875:24;;-1:-1:-1;;;;;282901:10:0;;;;282913:11;282829:17;:96::i;:::-;282964:20;;;;283012:9;;283023:10;;282936:98;;282954:8;;-1:-1:-1;;;;;282964:20:0;;;282986:24;;;;;;;-1:-1:-1;;;;;283012:9:0;;;;283023:10;282936:17;:98::i;:::-;283079:11;;;283092:16;;;283052:57;;;-1:-1:-1;;;;;;;;283092:16:0;;;;;;;283052:57;;;-1:-1:-1;;;;;283079:11:0;;;;283052:57;;;;;;;;;;;;;283152:10;;283164:20;;;;283125:60;;;-1:-1:-1;;;;;283164:20:0;;;283125:60;;;-1:-1:-1;;;;;283152:10:0;;;;283125:60;;;;;;;;;;;;;282492:701;;:::o;236267:194::-;236392:25;:61;;-1:-1:-1;;236392:61:0;;;;;;;;;;;;236267:194::o;51350:188::-;51464:7;51496:34;51505:17;51509:6;51517:4;51505:3;:17::i;:::-;51524:5;51496:8;:34::i;:::-;51489:41;51350:188;-1:-1:-1;;;;51350:188:0:o;77174:355::-;77291:8;;77256:9;;77303:2;77291:14;77283:23;;;;;;-1:-1:-1;77460:2:0;77453:10;77447:17;-1:-1:-1;;;;;77443:68:0;;77326:196::o;276598:486::-;276820:14;;276851:11;;276864:10;;276820:55;;;;;;-1:-1:-1;;;;;276851:11:0;;;276820:55;;;;276864:10;;;276820:55;;;;;;276697:7;;;;;;276820:14;;;:30;;:55;;;;;;;;;;276697:7;276820:14;:55;;;5:2:-1;;;;30:1;27;20:12;5:2;276820:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;276820:55:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;276820:55:0;;;;;;;276974:24;;276820:55;;-1:-1:-1;276820:55:0;-1:-1:-1;;;;276974:24:0;;;;276940:15;:31;;;:58;276932:94;;;;;-1:-1:-1;;;;;276932:94:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;277044:32;277060:9;277071:4;277044:15;:32::i;17452:284::-;-1:-1:-1;;;;;17540:22:0;;;;;;:12;:22;;;;;;:27;17537:192;;;17592:11;:18;17613:1;-1:-1:-1;17584:31:0;;;;;;-1:-1:-1;;;;;17630:22:0;;;;;;:12;:22;;;;;;17655:12;17630:37;;17687:30;;;17630:22;17687:30;17452:284;:::o;273770:2820::-;-1:-1:-1;;;;;274267:18:0;;273930;274267;;;:8;:18;;;;;273930;;;;;;;;;;;;;274377;274366:7;:5;:7::i;:::-;:29;;;;;;;;;274344:51;;274637:9;;;;;;;;;-1:-1:-1;;;;;274637:9:0;-1:-1:-1;;;;;274637:31:0;;274669:8;274679:18;;;;;;;;;-1:-1:-1;;;;;274679:18:0;274699:6;:19;;;;;;;;;;-1:-1:-1;;;;;274699:19:0;274720:12;274734:14;274637:112;;;;;-1:-1:-1;;;274637:112:0;;;;;;;-1:-1:-1;;;;;274637:112:0;-1:-1:-1;;;;;274637:112:0;;;;;;-1:-1:-1;;;;;274637:112:0;;;;;;-1:-1:-1;;;;;274637:112:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;274637:112:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;274637:112:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;274637:112:0;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;274637:112:0;;-1:-1:-1;274637:112:0;-1:-1:-1;274637:112:0;-1:-1:-1;274637:112:0;;-1:-1:-1;274637:112:0;-1:-1:-1;274788:46:0;;;;;;;;-1:-1:-1;;;;;274788:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;274940:19;;-1:-1:-1;;;;;274940:19:0;274925:34;;:54;-1:-1:-1;274925:54:0;;;:76;;;275000:1;274983:14;:18;274925:76;274917:106;;;;;;;-1:-1:-1;;;;;274917:106:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;275097:19;;-1:-1:-1;;;;;275097:19:0;275082:34;;:54;-1:-1:-1;275082:54:0;275074:84;;;;;-1:-1:-1;;;;;275074:84:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;275211;275225:14;275241;275257:23;275282:12;275211:13;:84::i;:::-;275210:85;275202:116;;;;;-1:-1:-1;;;;;275202:116:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;275535:14;275531:111;;;275574:13;:35;;;;275601:8;-1:-1:-1;;;;;275591:18:0;:6;-1:-1:-1;;;;;275591:18:0;;;275574:35;275566:64;;;;;;;-1:-1:-1;;;;;275566:64:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;275812:8;-1:-1:-1;;;;;275802:18:0;:6;-1:-1:-1;;;;;275802:18:0;;275798:703;;;275884:7;;:34;;;;;;;;;;;;;;275855:64;;-1:-1:-1;;;;;275884:7:0;;:20;;:34;;;;;;;;;;;;;;:7;;:34;;;5:2:-1;;;;30:1;27;20:12;275855:64:0;275837:82;-1:-1:-1;;275952:1:0;275938:15;;275934:556;;;276387:22;;-1:-1:-1;;276387:22:0;;;;:26;276373:40;;;276366:48;;;;276448:22;;-1:-1:-1;;276448:22:0;;;;:26;276433:41;;;;;275934:556;276542:12;;:40;;;;;;;;;;;;;;-1:-1:-1;;;;;276542:12:0;;;;:24;;:40;;;;;:12;;:40;;;;;;;;:12;;:40;;;5:2:-1;;;;30:1;27;20:12;5:2;276542:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;276542:40:0;;;;273770:2820;;;;;;;;;;;;;:::o;277092:2011::-;-1:-1:-1;;;;;277409:18:0;;277335:22;277409:18;;;:8;:18;;;;;277458:19;;-1:-1:-1;;;;;277458:19:0;:24;;;277683:34;;;;277335:22;;;;-1:-1:-1;;;277736:23:0;;277728:61;;;;;-1:-1:-1;;;;;277728:61:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;277923:11;277893:6;:20;;;;;;;;;;-1:-1:-1;;;;;277893:20:0;:42;277867:68;;278104:14;278073:6;:21;;;;;;;;;;-1:-1:-1;;;;;278073:21:0;:46;278046:73;;278317:6;:16;;;;;;;;;;-1:-1:-1;;;;;278317:16:0;278295:38;;278370:6;:20;;;;;;;;;;-1:-1:-1;;;;;278370:20:0;278344:46;;278427:10;;;;;;;;;-1:-1:-1;;;;;278427:10:0;-1:-1:-1;;;;;278405:33:0;:10;-1:-1:-1;;;;;278405:33:0;;278401:163;;;278455:34;;;;278401:163;;;278522:30;;;;278401:163;278669:24;;;;;;;;;;:46;;;278709:6;-1:-1:-1;;;;;278697:18:0;:8;-1:-1:-1;;;;;278697:18:0;;;278669:46;278801:45;;-1:-1:-1;;278801:45:0;;;-1:-1:-1;;;;;278801:45:0;;;;-1:-1:-1;;278857:39:0;;;-1:-1:-1;;;278857:39:0;;;;;;;;-1:-1:-1;278907:21:0;;:41;;;;;;;;;;;278959:31;;;;;;;;;;;;;;279001:20;;;;:39;;;;;;;;;;;;;-1:-1:-1;;279051:44:0;;;;;;;;;;;;;;;-1:-1:-1;277092:2011:0;;-1:-1:-1;;;;;;277092:2011:0:o;279111:1705::-;279467:15;;-1:-1:-1;;;279467:15:0;;;;:32;;279439:25;;;;;279537:5;279517:25;;279510:33;;;;279682:25;;;;;;;:59;;;-1:-1:-1;279827:19:0;:27;;279853:1;279827:27;;;279849:1;279827:27;279808:15;;:47;;;;;-1:-1:-1;;;279808:15:0;;;;:47;;-1:-1:-1;;;;;;279943:18:0;:33;;;-1:-1:-1;;;;279994:29:0;;279987:37;;;;-1:-1:-1;;280135:26:0;;280265:10;;-1:-1:-1;;;;;280135:26:0;;;;-1:-1:-1;;;280198:30:0;;;;-1:-1:-1;;;;;280265:10:0;280243;:33;280239:163;;;280293:34;;280239:163;;;280360:30;;;;280239:163;280482:15;:43;;-1:-1:-1;;280482:43:0;-1:-1:-1;;;280482:43:0;;;;;;;;;;280536:55;;;;;;;;;;;;;;;280602:35;;-1:-1:-1;;;280602:35:0;;;;;;;;;;;;;-1:-1:-1;;280648:50:0;;;-1:-1:-1;;;;;280648:50:0;;;;;;;280709:26;:40;;;;;;;;;-1:-1:-1;;280760:48:0;;;;;;;;;-1:-1:-1;;;;;279111:1705:0:o;265521:390::-;265782:22;;265631:19;;;;265782:22;;265770:9;:34;;;;;;;;;-1:-1:-1;;265879:23:0;;;265521:390;;;:::o;280824:832::-;281129:20;;281094:15;;-1:-1:-1;;;281094:15:0;;;;:32;;:55;281164:14;:33;;;;;281183:14;281182:15;281164:33;281160:206;;;-1:-1:-1;281321:33:0;;281266:25;;;;;;;;-1:-1:-1;;;281231:15:0;;;;:32;;:60;:87;;;:123;281160:206;281512:14;281511:15;:88;;;;-1:-1:-1;281567:30:0;;281546:18;;-1:-1:-1;;;;;281546:18:0;281531:33;;:66;281511:88;281506:143;;;-1:-1:-1;281633:4:0;280824:832;;;;;;:::o;265173:195::-;265309:7;;265257:4;;-1:-1:-1;;;;;265286:30:0;;;265309:7;;265286:30;:74;;;;;265320:35;265336:18;265320:15;:35::i;:::-;:40;;265173:195;-1:-1:-1;;265173:195:0:o;239804:1309::-;239898:6;;-1:-1:-1;;;239898:6:0;;;;239878:17;;239944:42;239898:6;239977:8;239944:22;:42::i;:::-;240066:6;:26;;239915:71;;-1:-1:-1;239915:71:0;;-1:-1:-1;;240066:26:0;-1:-1:-1;;;239915:71:0;240066:26;;;;;;;;;;;;;240167:25;240201:8;240193:17;;;;;;;;240167:44;;;;;;;;;;;;;;;;;;;;;;;;;;;240149:62;;240363:8;240337:34;;240344:15;240337:34;;;240333:101;;;-1:-1:-1;240406:15:0;240333:101;240561:8;240514:25;240548:8;240540:17;;;;;;;;240514:44;;;;;;;;;;;;;;;;;;:55;;;;;;;;;;;;;;;;;;240686:19;240714:17;240706:26;;;;;;;;240686:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;240675:8;:58;240619:25;240653:17;240645:26;;;;;;;;240619:53;;;;;;;;;;;;;;;;;;:114;;;;;;;;;;;;;;;;;;240781:45;240798:8;240808:17;240781:16;:45::i;:::-;240854:17;240844:27;;;;;;;;:6;;-1:-1:-1;;;240844:6:0;;;;:27;;;;;;;;;240837:35;;;;240950:19;;:66;;;;;-1:-1:-1;;;;;240950:19:0;;;;:37;;240988:8;;240998:17;;240950:66;;;240988:8;240950:66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;240950:66:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;240950:66:0;;;;241032:73;241058:8;241051:16;;;;;;;;241076:17;241069:25;;;;;;;;241032:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;239804:1309;;;;:::o;262879:988::-;262981:8;263346:16;263365:67;263379:5;263386:17;;263406:1;263386:21;263409:1;263412:19;;263365:13;:67::i;:::-;263346:86;;263447:11;263443:241;;;263491:18;263479:8;:30;;;;;;;;;263475:93;;;263537:15;263530:22;;;;263475:93;263598:15;263586:8;:27;;;;;;;;;263582:91;;;263641:16;263634:23;;;;263582:91;263710:16;263698:8;:28;;;;;;;;;:86;;;;-1:-1:-1;263730:40:0;;:54;;263698:86;263694:140;;;263808:14;263801:21;;;;263694:140;263851:8;263844:15;;262879:988;;;;;:::o;51969:179::-;52054:7;52091:5;;;52114:6;;;;52107:14;;;;52139:1;51969:179;-1:-1:-1;;;51969:179:0:o;283201:812::-;283480:9;;283424:11;;283420:50;;;283452:7;;283420:50;283492:6;283480:18;;283567:17;283563:356;;;283622:48;;;;;;283655:4;283622:48;;;;-1:-1:-1;;;;;283622:48:0;;;;;;;;;:32;;;;;;:48;;;;;;;;;;;-1:-1:-1;283622:32:0;:48;;;5:2:-1;;;;30:1;27;20:12;5:2;283622:48:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;283622:48:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;283622:48:0;;-1:-1:-1;283692:12:0;;;;283685:20;;;;283734:1;283724:7;:11;283720:188;;;283763:5;-1:-1:-1;;;;;283763:13:0;;283785;283801:7;283763:46;;;;;-1:-1:-1;;;283763:46:0;;;;;;;-1:-1:-1;;;;;283763:46:0;-1:-1:-1;;;;;283763:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;283763:46:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;283763:46:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;283763:46:0;283756:54;;;;;;283829:13;-1:-1:-1;;;;;283829:22:0;;283852:8;283829:32;;;;;-1:-1:-1;;;283829:32:0;;;;;;;-1:-1:-1;;;;;283829:32:0;-1:-1:-1;;;;;283829:32:0;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;283829:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;283829:32:0;;;;283885:7;283880:12;;;;283720:188;283937:1;283933;:5;283929:77;;;283962:31;;;-1:-1:-1;;;;;283962:31:0;;-1:-1:-1;;;;;283962:31:0;;;;;;;;;;;;;;;;;;-1:-1:-1;283962:31:0;;;;;;;;:14;;;;;;:31;;;;;;;;;;;;;;;;;;:14;:31;;;5:2:-1;;;;30:1;27;20:12;5:2;283962:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;283962:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;283962:31:0;283955:39;;;;;;283201:812;;;;;;;:::o;51605:193::-;51690:7;51727:5;;;51750:6;;;:20;;;51769:1;51764;51760;:5;;;;;;;;:10;51750:20;51743:28;;;;;50985:225;51089:7;51170:32;51181:6;51189:4;51195:6;51170:10;:32::i;263875:758::-;264003:8;264241:16;264229:8;:28;;;;;;;;;:70;;;;-1:-1:-1;264279:20:0;;264261:15;;-1:-1:-1;;;264261:15:0;;;;:38;264229:70;264225:125;;;-1:-1:-1;264323:15:0;264316:22;;264641:459;264759:16;264747:8;:28;;;;;;;;;264743:82;;;264792:21;:19;:21::i;:::-;264851:14;264839:8;:26;;;;;;;;;264835:78;;;264882:19;:17;:19::i;:::-;264939:15;264927:8;:27;;;;;;;;;264923:80;;;264971:20;:18;:20::i;:::-;265029:15;265017:8;:27;;;;;;;;;265013:80;;;265061:20;:18;:20::i;265960:4076::-;266024:24;266177:22;266809:32;266912:17;267401:23;267757:25;269174:30;269446:31;269843:27;266051:9;;;;;;;;;-1:-1:-1;;;;;266051:9:0;-1:-1:-1;;;;;266051:21:0;;:23;;;;;-1:-1:-1;;;266051:23:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;266051:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;266051:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;266051:23:0;266202:36;;;;;;;;266051:23;;-1:-1:-1;;;;;;266202:34:0;;;;;:36;;;;;266051:23;;266202:36;;;;;;;;;:34;:36;;;5:2:-1;;;;30:1;27;20:12;5:2;266202:36:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;266202:36:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;266202:36:0;266875:20;;266856:15;;266202:36;;-1:-1:-1;;;;266856:15:0;;;;;:39;266852:826;;;266932:10;-1:-1:-1;;;;;266932:31:0;;:33;;;;;-1:-1:-1;;;266932:33:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;266932:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;266932:33:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;266932:33:0;267195:20;;267183:32;;;-1:-1:-1;266932:33:0;-1:-1:-1;266852:826:0;;;267275:14;;267316:15;;267275:57;;;;;;-1:-1:-1;;;267316:15:0;;;;;267275:57;;;;;-1:-1:-1;;;;;267275:14:0;;;;:40;;:57;;;;;;;;;;;;;;;-1:-1:-1;267275:14:0;:57;;;5:2:-1;;;;30:1;27;20:12;5:2;267275:57:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;267275:57:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;267275:57:0;267428:15;;267275:57;;-1:-1:-1;267474:14:0;;-1:-1:-1;;;267428:15:0;;;;:42;;267427:61;;;;;;;;267401:87;;267525:1;267507:15;:19;267503:164;;;267636:15;267619:14;:32;267591:60;;;;267503:164;267785:15;;-1:-1:-1;;;267785:15:0;;;;:42;;;-1:-1:-1;268247:14:0;267785:42;268227:34;;;;;;;268207:10;:55;;;268362:60;;;;;-1:-1:-1;;268227:34:0;;;;268207:55;-1:-1:-1;;268207:55:0;;;;268362:60;;;;;;;268712:14;;268748:26;;268712:63;;;;;;-1:-1:-1;;;;;268748:26:0;;;268712:63;;;;;-1:-1:-1;;;;;268712:14:0;;;;:35;;:63;;;;;;;;;;268207:10;268712:14;:63;;;5:2:-1;;;;30:1;27;20:12;5:2;268712:63:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;268712:63:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;268712:63:0;;268686:90;;;;-1:-1:-1;;;;;;;;268686:90:0;;;;;;;268817:14;;268853:30;;268817:67;;;;;;-1:-1:-1;;;268853:30:0;;;;;;268817:67;;;;;;-1:-1:-1;;;;;268817:14:0;;;;:35;;:67;;;;;268712:63;;268817:67;;;;;-1:-1:-1;268817:14:0;:67;;;5:2:-1;;;;30:1;27;20:12;5:2;268817:67:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;268817:67:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;268817:67:0;268787:19;:98;;-1:-1:-1;;268787:98:0;;;-1:-1:-1;;;;;268787:98:0;;;;;;;;268817:67;269002:15;;268972:26;:45;;-1:-1:-1;;268972:45:0;;;-1:-1:-1;;;269002:15:0;;;;268972:45;;;;;;;;;;269028:53;;;269062:19;;;-1:-1:-1;;;269028:53:0;;;;;;;;;;;;;;;;;;;;269207:41;;;;;;;;269251:10;;;;;-1:-1:-1;;;;;269207:39:0;;;;;:41;;;;;;;;;;;-1:-1:-1;269207:39:0;:41;;;5:2:-1;;;;30:1;27;20:12;5:2;269207:41:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;269207:41:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;269207:41:0;269516:30;;269207:54;;;-1:-1:-1;269488:59:0;;269207:54;;-1:-1:-1;;;269516:30:0;;-1:-1:-1;;;;;269516:30:0;269488:3;:59::i;:::-;269566:10;;269586:7;;269566:57;;;-1:-1:-1;;;;;269566:57:0;;-1:-1:-1;;;;;269586:7:0;;;269566:57;;;;-1:-1:-1;;;;;269566:57:0;;;;;;;;;;;:10;:57;;;;;;;;269446:102;;-1:-1:-1;269566:10:0;;;;;:19;;:57;;;;;;;;;;;;;;;;;;:10;:57;;;5:2:-1;;;;30:1;27;20:12;5:2;269566:57:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;269566:57:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;269566:57:0;269559:65;;;;;;269714:30;:57;;-1:-1:-1;;;;;;;;269714:57:0;;;;;;;;;;;;-1:-1:-1;;269714:57:0;;;;;;269913:10;;;269873:37;;;;;;;;269913:10;;;;;-1:-1:-1;;;;;269873:35:0;;;;;:37;;;;;269913:10;269873:37;;;;;;-1:-1:-1;269873:35:0;:37;;;5:2:-1;;;;30:1;27;20:12;5:2;269873:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;269873:37:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;269873:37:0;269957:7;;269966:28;;269873:37;269996:10;;269939:89;;;-1:-1:-1;;;;;269957:7:0;;;269939:89;;269966:28;;;;269939:89;;;;;;;269996:10;;269939:89;;;;269873:50;;;;269939:89;;;;;;;;269873:50;;-1:-1:-1;269939:89:0;;;;;;;;;265960:4076;;;;;;;;;:::o;270092:1211::-;270222:7;;:23;;;;;;270240:4;270222:23;;;;;;270202:17;;;;-1:-1:-1;;;;;270222:7:0;;;;:17;;:23;;;;;;;;;;;;;;;270202:17;270222:7;:23;;;5:2:-1;;;;30:1;27;20:12;5:2;270222:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;270222:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;270222:23:0;;-1:-1:-1;270281:39:0;270222:23;270281:28;:39::i;:::-;-1:-1:-1;270449:7:0;;270466:23;;270449:58;;;-1:-1:-1;;;;;270449:58:0;;-1:-1:-1;;;;;270466:23:0;;;270449:58;;;;;;;;;;;;;;;:7;:58;;;;;;;;270256:64;;-1:-1:-1;270449:7:0;;;;;:16;;:58;;;;;;;;;;;;;;;;;;:7;:58;;;5:2:-1;;;;30:1;27;20:12;5:2;270449:58:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;270449:58:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;270449:58:0;270442:66;;;;;;270568:26;;270597:1;-1:-1:-1;;;;;270568:26:0;;;:30;270564:153;;;270622:11;;270643:28;;270673:26;;270622:82;;;-1:-1:-1;;;;;270622:82:0;;-1:-1:-1;;;;;270643:28:0;;;270622:82;;;;-1:-1:-1;;;;;270673:26:0;;;270622:82;;;;;;;;;:11;:82;;;;;;;;:11;;;;;:20;;:82;;;;;;;;;;;;;;;;;:11;:82;;;5:2:-1;;;;30:1;27;20:12;5:2;270622:82:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;270622:82:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;270622:82:0;270615:90;;;;;;270733:30;;270766:1;-1:-1:-1;;;270733:30:0;;;-1:-1:-1;;;;;270733:30:0;:34;270729:160;;;270791:10;;270811:28;;270841:30;;270791:85;;;-1:-1:-1;;;;;270791:85:0;;-1:-1:-1;;;;;270811:28:0;;;270791:85;;;;-1:-1:-1;;;270841:30:0;;;-1:-1:-1;;;;;270841:30:0;270791:85;;;;;;;;;-1:-1:-1;270791:85:0;;;;;;;;:10;;;;;:19;;:85;;;;;;;;;;;;;;;;;:10;:85;;;5:2:-1;;;;30:1;27;20:12;5:2;270791:85:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;270791:85:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;270791:85:0;270784:93;;;;;;270932:12;;270957:25;;270932:51;;;;;;270957:25;;;;;;270932:51;;;;;-1:-1:-1;;;;;270932:12:0;;;;:24;;:51;;;;;-1:-1:-1;;270932:51:0;;;;;;;;-1:-1:-1;270932:12:0;:51;;;5:2:-1;;;;30:1;27;20:12;5:2;270932:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;271020:23:0;;270999:69;;;-1:-1:-1;;;;;271020:23:0;;;270999:69;;;;;;;;;;;;;;;;;-1:-1:-1;270999:69:0;;;;;;;-1:-1:-1;270999:69:0;271110:28;;271140:11;;271153:26;;271084:96;;;-1:-1:-1;;;;;271110:28:0;;;271084:96;;271140:11;;;;271084:96;;;;-1:-1:-1;;;;;271153:26:0;271084:96;;;;;;;;;;;;;;;271222:28;;271252:10;;271264:30;;271196:99;;;-1:-1:-1;;;;;271222:28:0;;;271196:99;;271252:10;;;;271196:99;;;;-1:-1:-1;;;271264:30:0;;-1:-1:-1;;;;;271264:30:0;271196:99;;;;;;;;;;;;;;;270092:1211;;:::o;271359:600::-;271554:7;;:23;;;;;;271572:4;271554:23;;;;;;271533:18;;;;-1:-1:-1;;;;;271554:7:0;;;;:17;;:23;;;;;;;;;;;;;;;271533:18;271554:7;:23;;;5:2:-1;;;;30:1;27;20:12;5:2;271554:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;271554:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;271554:23:0;271614:12;;:28;;;;;;271637:4;271614:28;;;;;;271554:23;;-1:-1:-1;;;;;;271614:12:0;;;;:22;;:28;;;;;271554:23;;271614:28;;;;;;;;:12;;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;271614:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;271614:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;271614:28:0;;-1:-1:-1;271670:1:0;271657:14;;271653:71;;;271688:7;;:24;;;;;;;;;;;;;;-1:-1:-1;;;;;271688:7:0;;;;:12;;:24;;;;;:7;;:24;;;;;;;;:7;;:24;;;5:2:-1;;;;30:1;27;20:12;5:2;271688:24:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;271688:24:0;;;;271653:71;271804:1;271786:15;:19;271782:95;;;271822:12;;:43;;;;;;;;;;;;;;-1:-1:-1;;;;;271822:12:0;;;;:26;;:43;;;;;:12;;:43;;;;;;;;:12;;:43;;;5:2:-1;;;;30:1;27;20:12;5:2;271822:43:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;271822:43:0;;;;271782:95;271909:12;;271892:59;;;-1:-1:-1;;;;;271909:12:0;;;271892:59;;;;;;;;;;;;;;;;;;;;;;;;271359:600;;:::o;272016:1746::-;272155:23;272272:25;272389:30;272195:8;;;;;;;;;-1:-1:-1;;;;;272195:8:0;-1:-1:-1;;;;;272195:21:0;;:23;;;;;-1:-1:-1;;;272195:23:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;272195:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;272195:23:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;272195:23:0;;-1:-1:-1;;;;;;272237:23:0;;;;272230:31;;;;272300:8;;;;;;;;;-1:-1:-1;;;;;272300:8:0;-1:-1:-1;;;;;272300:26:0;;:28;;;;;-1:-1:-1;;;272300:28:0;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;272300:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;272300:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;272300:28:0;;-1:-1:-1;;;;;;272346:31:0;;;;272339:39;;;;-1:-1:-1;272447:7:0;;272422:34;;;-1:-1:-1;;;;;272447:7:0;;;-1:-1:-1;;;272422:34:0;;;;;;;;;;;;26:21:-1;;;22:32;6:49;;272422:34:0;;;;;;;272471:15;272447:7;-1:-1:-1;;;272471:15:0;;;-1:-1:-1;;;;;272471:15:0;:19;272467:231;;;272618:11;;272650:15;;;272618:67;;-1:-1:-1;;;;;272618:67:0;;-1:-1:-1;;;;;272618:67:0;;;;;;;;;-1:-1:-1;;;272650:15:0;;;-1:-1:-1;;;;;272650:15:0;272618:67;;;;;;;;;;;;;;;;;;;;;:11;;;;;:20;;:67;;272650:15;;272618:67;;;;;;;;;;;;;-1:-1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;272618:67:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;272618:67:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;272618:67:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;272618:67:0;272611:75;;;;;;272712:19;;272734:1;-1:-1:-1;;;;;272712:19:0;;;:23;272708:170;;;272795:10;;272826:19;;272795:70;;-1:-1:-1;;;;;272795:70:0;;-1:-1:-1;;;;;272795:70:0;;;;;;;;;-1:-1:-1;;;;;272826:19:0;;;272795:70;;;;;;;;;;;;;;;;;;;;;:10;;;;;:19;;272815:9;;272826:19;;272847:17;;272795:70;;;;;;;;;;;;;:10;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;272795:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;272795:70:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;272795:70:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;272795:70:0;272788:78;;;;;;273418:12;;273467:25;;273418:75;;;;;;-1:-1:-1;;;;;273418:75:0;;;;;;;273467:25;;;;;;273418:75;;;;;;:12;;;;;:29;;:75;;;;;-1:-1:-1;;273418:75:0;;;;;;;-1:-1:-1;273418:12:0;:75;;;5:2:-1;;;;30:1;27;20:12;5:2;273418:75:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;273532:11:0;;273556:15;;;273511:61;;;-1:-1:-1;;;;;273532:11:0;;;273511:61;;;;;;;;;;;;-1:-1:-1;;;;;;;;273556:15:0;;;;273511:61;;;;;;;-1:-1:-1;273511:61:0;;;;;;;-1:-1:-1;273511:61:0;273609:10;;273632:19;;273588:64;;;-1:-1:-1;;;;;273609:10:0;;;273588:64;;;;;;;;;-1:-1:-1;;;;;273632:19:0;;;273588:64;;;;;;;;;;;;;;273695:12;;273728:25;;;273668:86;;;-1:-1:-1;;;;;273695:12:0;;;273668:86;;;;;;;;;;;;273728:25;;;;;;273668:86;;;;;;;;;;;;;;272016:1746;;;:::o;52156:138::-;52241:7;52277:1;52273;:5;:13;;52285:1;52273:13;;;-1:-1:-1;52281:1:0;;52156:138;-1:-1:-1;52156:138:0:o;243248:40768::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;243248:40768:0;;;-1:-1:-1;243248:40768:0;:::i;:::-;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;-1:-1;243248:40768:0;;;-1:-1:-1;;243248:40768:0:o;:::-;;;;;;;;;;-1:-1:-1;243248:40768:0;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;
Swarm Source
bzzr://56a74eca5a01dd4fa58a7c81c8c45ebe3df29b0fa05f3d1740f03e897744add5
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $0.009655 | 35,596.9992 | $343.67 |
Loading...
Loading
[ 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.