Transaction Hash:
Block:
15648404 at Sep-30-2022 08:00:59 PM +UTC
Transaction Fee:
0.00388099879854691 ETH
$10.27
Gas Used:
148,249 Gas / 26.17892059 Gwei
Emitted Events:
100 |
MaticToken.Transfer( from=[Sender] 0x2a8cf8defcee248a9238a3254594077c6c3c280e, to=[Receiver] DepositManagerProxy, value=282286890000000000000 )
|
101 |
MaticToken.Approval( owner=[Sender] 0x2a8cf8defcee248a9238a3254594077c6c3c280e, spender=[Receiver] DepositManagerProxy, value=115792089237316195423570985008687907853269984665640564039175297117913129639935 )
|
102 |
StateSender.StateSynced( id=2342613, contractAddress=0xD9c7C4ED...55Fe34861, data=0x0000000000000000000000002A8CF8DEFCEE248A9238A3254594077C6C3C280E0000000000000000000000007D1AFA7B718FB893DB30A3ABC0CFC608AACFEBB000000000000000000000000000000000000000000000000F4D847ED5BFC4A0000000000000000000000000000000000000000000000000000000000016265191 )
|
103 |
DepositManagerProxy.NewDepositBlock( owner=[Sender] 0x2a8cf8defcee248a9238a3254594077c6c3c280e, token=MaticToken, amountOrNFTId=282286890000000000000, depositBlockId=371610001 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x199D5ED7...6e95B253F
Miner
| (bloXroute: Regulated Builder) | 4.057384855866773387 Eth | 4.057688707850925763 Eth | 0.000303851984152376 | |
0x28e4F3a7...189A5bFbE | (Polygon (Matic): State Syncer) | ||||
0x2A8cf8dE...C6C3C280E |
0.00795009485027063 Eth
Nonce: 7
|
0.00406909605172372 Eth
Nonce: 8
| 0.00388099879854691 | ||
0x401F6c98...51321188b | (Polygon (Matic): Plasma Bridge) | ||||
0x7D1AfA7B...8AaCfeBB0 | |||||
0x86E4Dc95...23894C287 | (Polygon (Matic): Root Chain Proxy) |
Execution Trace
DepositManagerProxy.8b9e4f93( )
DepositManager.depositERC20ForUser( _token=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0, _user=0x2A8cf8dEFcEe248A9238A3254594077C6C3C280E, _amount=282286890000000000000 )
-
MaticToken.transferFrom( from=0x2A8cf8dEFcEe248A9238A3254594077C6C3C280E, to=0x401F6c983eA34274ec46f84D70b31C151321188b, value=282286890000000000000 ) => ( True )
-
Registry.isTokenMapped( _token=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0 ) => ( True )
RootChainProxy.5391f483( )
RootChain.updateDepositId( numDeposits=1 ) => ( depositId=371610001 )
-
Registry.STATICCALL( )
-
-
StateSender.syncState( receiver=0xD9c7C4ED4B66858301D0cb28Cc88bf655Fe34861, data=0x0000000000000000000000002A8CF8DEFCEE248A9238A3254594077C6C3C280E0000000000000000000000007D1AFA7B718FB893DB30A3ABC0CFC608AACFEBB000000000000000000000000000000000000000000000000F4D847ED5BFC4A0000000000000000000000000000000000000000000000000000000000016265191 )
-
depositERC20ForUser[DepositManager (ln:1623)]
safeTransferFrom[DepositManager (ln:1629)]
_safeCreateDepositBlock[DepositManager (ln:1630)]
_createDepositBlock[DepositManager (ln:1657)]
DepositBlock[DepositManager (ln:1671)]
syncState[DepositManager (ln:1672)]
encode[DepositManager (ln:1672)]
NewDepositBlock[DepositManager (ln:1673)]
updateDepositId[DepositManager (ln:1661)]
File 1 of 7: DepositManagerProxy
File 2 of 7: MaticToken
File 3 of 7: StateSender
File 4 of 7: DepositManager
File 5 of 7: Registry
File 6 of 7: RootChainProxy
File 7 of 7: RootChain
// File: contracts/common/governance/IGovernance.sol pragma solidity ^0.5.2; interface IGovernance { function update(address target, bytes calldata data) external; } // File: contracts/common/governance/Governable.sol pragma solidity ^0.5.2; contract Governable { IGovernance public governance; constructor(address _governance) public { governance = IGovernance(_governance); } modifier onlyGovernance() { require( msg.sender == address(governance), "Only governance contract is authorized" ); _; } } // File: contracts/root/withdrawManager/IWithdrawManager.sol pragma solidity ^0.5.2; contract IWithdrawManager { function createExitQueue(address token) external; function verifyInclusion( bytes calldata data, uint8 offset, bool verifyTxInclusion ) external view returns (uint256 age); function addExitToQueue( address exitor, address childToken, address rootToken, uint256 exitAmountOrTokenId, bytes32 txHash, bool isRegularExit, uint256 priority ) external; function addInput( uint256 exitId, uint256 age, address utxoOwner, address token ) external; function challengeExit( uint256 exitId, uint256 inputId, bytes calldata challengeData, address adjudicatorPredicate ) external; } // File: contracts/common/Registry.sol pragma solidity ^0.5.2; contract Registry is Governable { // @todo hardcode constants bytes32 private constant WETH_TOKEN = keccak256("wethToken"); bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager"); bytes32 private constant STAKE_MANAGER = keccak256("stakeManager"); bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare"); bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager"); bytes32 private constant CHILD_CHAIN = keccak256("childChain"); bytes32 private constant STATE_SENDER = keccak256("stateSender"); bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager"); address public erc20Predicate; address public erc721Predicate; mapping(bytes32 => address) public contractMap; mapping(address => address) public rootToChildToken; mapping(address => address) public childToRootToken; mapping(address => bool) public proofValidatorContracts; mapping(address => bool) public isERC721; enum Type {Invalid, ERC20, ERC721, Custom} struct Predicate { Type _type; } mapping(address => Predicate) public predicates; event TokenMapped(address indexed rootToken, address indexed childToken); event ProofValidatorAdded(address indexed validator, address indexed from); event ProofValidatorRemoved(address indexed validator, address indexed from); event PredicateAdded(address indexed predicate, address indexed from); event PredicateRemoved(address indexed predicate, address indexed from); event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract); constructor(address _governance) public Governable(_governance) {} function updateContractMap(bytes32 _key, address _address) external onlyGovernance { emit ContractMapUpdated(_key, contractMap[_key], _address); contractMap[_key] = _address; } /** * @dev Map root token to child token * @param _rootToken Token address on the root chain * @param _childToken Token address on the child chain * @param _isERC721 Is the token being mapped ERC721 */ function mapToken( address _rootToken, address _childToken, bool _isERC721 ) external onlyGovernance { require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS"); rootToChildToken[_rootToken] = _childToken; childToRootToken[_childToken] = _rootToken; isERC721[_rootToken] = _isERC721; IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken); emit TokenMapped(_rootToken, _childToken); } function addErc20Predicate(address predicate) public onlyGovernance { require(predicate != address(0x0), "Can not add null address as predicate"); erc20Predicate = predicate; addPredicate(predicate, Type.ERC20); } function addErc721Predicate(address predicate) public onlyGovernance { erc721Predicate = predicate; addPredicate(predicate, Type.ERC721); } function addPredicate(address predicate, Type _type) public onlyGovernance { require(predicates[predicate]._type == Type.Invalid, "Predicate already added"); predicates[predicate]._type = _type; emit PredicateAdded(predicate, msg.sender); } function removePredicate(address predicate) public onlyGovernance { require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist"); delete predicates[predicate]; emit PredicateRemoved(predicate, msg.sender); } function getValidatorShareAddress() public view returns (address) { return contractMap[VALIDATOR_SHARE]; } function getWethTokenAddress() public view returns (address) { return contractMap[WETH_TOKEN]; } function getDepositManagerAddress() public view returns (address) { return contractMap[DEPOSIT_MANAGER]; } function getStakeManagerAddress() public view returns (address) { return contractMap[STAKE_MANAGER]; } function getSlashingManagerAddress() public view returns (address) { return contractMap[SLASHING_MANAGER]; } function getWithdrawManagerAddress() public view returns (address) { return contractMap[WITHDRAW_MANAGER]; } function getChildChainAndStateSender() public view returns (address, address) { return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]); } function isTokenMapped(address _token) public view returns (bool) { return rootToChildToken[_token] != address(0x0); } function isTokenMappedAndIsErc721(address _token) public view returns (bool) { require(isTokenMapped(_token), "TOKEN_NOT_MAPPED"); return isERC721[_token]; } function isTokenMappedAndGetPredicate(address _token) public view returns (address) { if (isTokenMappedAndIsErc721(_token)) { return erc721Predicate; } return erc20Predicate; } function isChildTokenErc721(address childToken) public view returns (bool) { address rootToken = childToRootToken[childToken]; require(rootToken != address(0x0), "Child token is not mapped"); return isERC721[rootToken]; } } // File: solidity-rlp/contracts/RLPReader.sol /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ pragma solidity ^0.5.0; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint len; uint memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint ptr = self.nextPtr; uint itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param item RLP encoded bytes */ function rlpLen(RLPItem memory item) internal pure returns (uint) { return item.len; } /* * @param item RLP encoded bytes */ function payloadLen(RLPItem memory item) internal pure returns (uint) { return item.len - _payloadOffset(item.memPtr); } /* * @param item RLP encoded list in bytes */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint memPtr = item.memPtr + _payloadOffset(item.memPtr); uint dataLen; for (uint i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint result; uint memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } return result == 0 ? false : true; } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(toUint(item)); } function toUint(RLPItem memory item) internal pure returns (uint) { require(item.len > 0 && item.len <= 33); uint offset = _payloadOffset(item.memPtr); uint len = item.len - offset; uint result; uint memPtr = item.memPtr + offset; assembly { result := mload(memPtr) // shfit to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint) { // one byte prefix require(item.len == 33); uint result; uint memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); uint offset = _payloadOffset(item.memPtr); uint len = item.len - offset; // data length bytes memory result = new bytes(len); uint destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint) { if (item.len == 0) return 0; uint count = 0; uint currPtr = item.memPtr + _payloadOffset(item.memPtr); uint endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint memPtr) private pure returns (uint) { uint itemLen; uint byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) itemLen = 1; else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint memPtr) private pure returns (uint) { uint byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint src, uint dest, uint len) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } // left over bytes. Mask is used to remove unwanted bytes from the word uint mask = 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.2; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/common/misc/ProxyStorage.sol pragma solidity ^0.5.2; contract ProxyStorage is Ownable { address internal proxyTo; } // File: contracts/common/mixin/ChainIdMixin.sol pragma solidity ^0.5.2; contract ChainIdMixin { bytes constant public networkId = hex"89"; uint256 constant public CHAINID = 137; } // File: contracts/root/RootChainStorage.sol pragma solidity ^0.5.2; contract RootChainHeader { event NewHeaderBlock( address indexed proposer, uint256 indexed headerBlockId, uint256 indexed reward, uint256 start, uint256 end, bytes32 root ); // housekeeping event event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId); struct HeaderBlock { bytes32 root; uint256 start; uint256 end; uint256 createdAt; address proposer; } } contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin { bytes32 public heimdallId; uint8 public constant VOTE_TYPE = 2; uint16 internal constant MAX_DEPOSITS = 10000; uint256 public _nextHeaderBlock = MAX_DEPOSITS; uint256 internal _blockDepositId = 1; mapping(uint256 => HeaderBlock) public headerBlocks; Registry internal registry; } // File: contracts/staking/stakeManager/IStakeManager.sol pragma solidity ^0.5.2; contract IStakeManager { // validator replacement function startAuction(uint256 validatorId, uint256 amount) external; function confirmAuctionBid( uint256 validatorId, uint256 heimdallFee, bool acceptDelegation, bytes calldata signerPubkey ) external; function transferFunds( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); function delegationDeposit( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); function stake( uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes calldata signerPubkey ) external; function unstake(uint256 validatorId) external; function totalStakedFor(address addr) external view returns (uint256); function supportsHistory() external pure returns (bool); function stakeFor( address user, uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes memory signerPubkey ) public; function checkSignatures( uint256 blockInterval, bytes32 voteHash, bytes32 stateRoot, address proposer, bytes memory sigs ) public returns (uint256); function updateValidatorState(uint256 validatorId, int256 amount) public; function ownerOf(uint256 tokenId) public view returns (address); function slash(bytes memory slashingInfoList) public returns (uint256); function validatorStake(uint256 validatorId) public view returns (uint256); function epoch() public view returns (uint256); function withdrawalDelay() public view returns (uint256); } // File: contracts/root/IRootChain.sol pragma solidity ^0.5.2; interface IRootChain { function slash() external; function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external; function getLastChildBlock() external view returns (uint256); function currentHeaderBlock() external view returns (uint256); } // File: contracts/root/RootChain.sol pragma solidity ^0.5.2; contract RootChain is RootChainStorage, IRootChain { using SafeMath for uint256; using RLPReader for bytes; using RLPReader for RLPReader.RLPItem; modifier onlyDepositManager() { require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY"); _; } function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external { (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256)); require(CHAINID == _borChainID, "Invalid bor chain id"); require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA"); // check if it is better to keep it in local storage instead IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress()); uint256 _reward = stakeManager.checkSignatures( end.sub(start).add(1), /** prefix 01 to data 01 represents positive vote on data and 00 is negative vote malicious validator can try to send 2/3 on negative vote so 01 is appended */ keccak256(abi.encodePacked(bytes(hex"01"), data)), accountHash, proposer, sigs ); require(_reward != 0, "Invalid checkpoint"); emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash); _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS); _blockDepositId = 1; } function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) { depositId = currentHeaderBlock().add(_blockDepositId); // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1) _blockDepositId = _blockDepositId.add(numDeposits); require( // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed _blockDepositId <= MAX_DEPOSITS, "TOO_MANY_DEPOSITS" ); } function getLastChildBlock() external view returns (uint256) { return headerBlocks[currentHeaderBlock()].end; } function slash() external { //TODO: future implementation } function currentHeaderBlock() public view returns (uint256) { return _nextHeaderBlock.sub(MAX_DEPOSITS); } function _buildHeaderBlock( address proposer, uint256 start, uint256 end, bytes32 rootHash ) private returns (bool) { uint256 nextChildBlock; /* The ID of the 1st header block is MAX_DEPOSITS. if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0 */ if (_nextHeaderBlock > MAX_DEPOSITS) { nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1; } if (nextChildBlock != start) { return false; } HeaderBlock memory headerBlock = HeaderBlock({ root: rootHash, start: nextChildBlock, end: end, createdAt: now, proposer: proposer }); headerBlocks[_nextHeaderBlock] = headerBlock; return true; } // Housekeeping function. @todo remove later function setNextHeaderBlock(uint256 _value) public onlyOwner { require(_value % MAX_DEPOSITS == 0, "Invalid value"); for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) { delete headerBlocks[i]; } _nextHeaderBlock = _value; _blockDepositId = 1; emit ResetHeaderBlock(msg.sender, _nextHeaderBlock); } // Housekeeping function. @todo remove later function setHeimdallId(string memory _heimdallId) public onlyOwner { heimdallId = keccak256(abi.encodePacked(_heimdallId)); } } // File: contracts/root/stateSyncer/StateSender.sol pragma solidity ^0.5.2; contract StateSender is Ownable { using SafeMath for uint256; uint256 public counter; mapping(address => address) public registrations; event NewRegistration( address indexed user, address indexed sender, address indexed receiver ); event RegistrationUpdated( address indexed user, address indexed sender, address indexed receiver ); event StateSynced( uint256 indexed id, address indexed contractAddress, bytes data ); modifier onlyRegistered(address receiver) { require(registrations[receiver] == msg.sender, "Invalid sender"); _; } function syncState(address receiver, bytes calldata data) external onlyRegistered(receiver) { counter = counter.add(1); emit StateSynced(counter, receiver, data); } // register new contract for state sync function register(address sender, address receiver) public { require( isOwner() || registrations[receiver] == msg.sender, "StateSender.register: Not authorized to register" ); registrations[receiver] = sender; if (registrations[receiver] == address(0)) { emit NewRegistration(msg.sender, sender, receiver); } else { emit RegistrationUpdated(msg.sender, sender, receiver); } } } // File: contracts/common/mixin/Lockable.sol pragma solidity ^0.5.2; contract Lockable is Governable { bool public locked; modifier onlyWhenUnlocked() { require(!locked, "Is Locked"); _; } constructor(address _governance) public Governable(_governance) {} function lock() external onlyGovernance { locked = true; } function unlock() external onlyGovernance { locked = false; } } // File: contracts/root/depositManager/DepositManagerStorage.sol pragma solidity ^0.5.2; contract DepositManagerHeader { event NewDepositBlock(address indexed owner, address indexed token, uint256 amountOrNFTId, uint256 depositBlockId); event MaxErc20DepositUpdate(uint256 indexed oldLimit, uint256 indexed newLimit); struct DepositBlock { bytes32 depositHash; uint256 createdAt; } } contract DepositManagerStorage is ProxyStorage, Lockable, DepositManagerHeader { Registry public registry; RootChain public rootChain; StateSender public stateSender; mapping(uint256 => DepositBlock) public deposits; address public childChain; uint256 public maxErc20Deposit = 100 * (10**18); } // File: contracts/common/misc/ERCProxy.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.5.2; // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md interface ERCProxy { function proxyType() external pure returns (uint256 proxyTypeId); function implementation() external view returns (address codeAddr); } // File: contracts/common/misc/DelegateProxy.sol pragma solidity ^0.5.2; contract DelegateProxy is ERCProxy { function proxyType() external pure returns (uint256 proxyTypeId) { // Upgradeable proxy proxyTypeId = 2; } function implementation() external view returns (address); function delegatedFwd(address _dst, bytes memory _calldata) internal { // solium-disable-next-line security/no-inline-assembly assembly { let result := delegatecall( sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0 ) let size := returndatasize let ptr := mload(0x40) returndatacopy(ptr, 0, size) // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. // if the call returned error data, forward it switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } } // File: contracts/common/misc/Proxy.sol pragma solidity ^0.5.2; contract Proxy is ProxyStorage, DelegateProxy { event ProxyUpdated(address indexed _new, address indexed _old); event OwnerUpdate(address _prevOwner, address _newOwner); constructor(address _proxyTo) public { updateImplementation(_proxyTo); } function() external payable { // require(currentContract != 0, "If app code has not been set yet, do not call"); // Todo: filter out some calls or handle in the end fallback delegatedFwd(proxyTo, msg.data); } function implementation() external view returns (address) { return proxyTo; } function updateImplementation(address _newProxyTo) public onlyOwner { require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS"); require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT"); emit ProxyUpdated(_newProxyTo, proxyTo); proxyTo = _newProxyTo; } function isContract(address _target) internal view returns (bool) { if (_target == address(0)) { return false; } uint256 size; assembly { size := extcodesize(_target) } return size > 0; } } // File: contracts/root/depositManager/DepositManagerProxy.sol pragma solidity ^0.5.2; contract DepositManagerProxy is Proxy, DepositManagerStorage { constructor( address _proxyTo, address _registry, address _rootChain, address _governance ) public Proxy(_proxyTo) Lockable(_governance) { registry = Registry(_registry); rootChain = RootChain(_rootChain); } }
File 2 of 7: MaticToken
pragma solidity 0.5.2; // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // File: openzeppelin-solidity/contracts/math/SafeMath.sol /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md * Originally based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for * all accounts just by listening to said events. Note that this isn't required by the specification, and other * compliant implementations may not do it. */ contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another. * Note that while this function emits an Approval event, this is not required as per the specification, * and other compliant implementations may not emit the event. * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 value) public returns (bool) { _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); _transfer(from, to, value); emit Approval(from, msg.sender, _allowed[from][msg.sender]); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Transfer token for a specified addresses * @param from The address to transfer from. * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address from, address to, uint256 value) internal { require(to != address(0)); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param value The amount that will be created. */ function _mint(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.add(value); _balances[account] = _balances[account].add(value); emit Transfer(address(0), account, value); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * Emits an Approval event (reflecting the reduced allowance). * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); _burn(account, value); emit Approval(account, msg.sender, _allowed[account][msg.sender]); } } // File: openzeppelin-solidity/contracts/access/Roles.sol /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } // File: openzeppelin-solidity/contracts/access/roles/PauserRole.sol contract PauserRole { using Roles for Roles.Role; event PauserAdded(address indexed account); event PauserRemoved(address indexed account); Roles.Role private _pausers; constructor () internal { _addPauser(msg.sender); } modifier onlyPauser() { require(isPauser(msg.sender)); _; } function isPauser(address account) public view returns (bool) { return _pausers.has(account); } function addPauser(address account) public onlyPauser { _addPauser(account); } function renouncePauser() public { _removePauser(msg.sender); } function _addPauser(address account) internal { _pausers.add(account); emit PauserAdded(account); } function _removePauser(address account) internal { _pausers.remove(account); emit PauserRemoved(account); } } // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is PauserRole { event Paused(address account); event Unpaused(address account); bool private _paused; constructor () internal { _paused = false; } /** * @return true if the contract is paused, false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!_paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(_paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyPauser whenNotPaused { _paused = true; emit Paused(msg.sender); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyPauser whenPaused { _paused = false; emit Unpaused(msg.sender); } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol /** * @title Pausable token * @dev ERC20 modified with pausable transfers. **/ contract ERC20Pausable is ERC20, Pausable { function transfer(address to, uint256 value) public whenNotPaused returns (bool) { return super.transfer(to, value); } function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) { return super.transferFrom(from, to, value); } function approve(address spender, uint256 value) public whenNotPaused returns (bool) { return super.approve(spender, value); } function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool success) { return super.increaseAllowance(spender, addedValue); } function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool success) { return super.decreaseAllowance(spender, subtractedValue); } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol /** * @title ERC20Detailed token * @dev The decimals are only for visualization purposes. * All the operations are done using the smallest and indivisible token unit, * just as on Ethereum all the operations are done in wei. */ contract ERC20Detailed is IERC20 { string private _name; string private _symbol; uint8 private _decimals; constructor (string memory name, string memory symbol, uint8 decimals) public { _name = name; _symbol = symbol; _decimals = decimals; } /** * @return the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @return the symbol of the token. */ function symbol() public view returns (string memory) { return _symbol; } /** * @return the number of decimals of the token. */ function decimals() public view returns (uint8) { return _decimals; } } // File: contracts/MaticToken.sol contract MaticToken is ERC20Pausable, ERC20Detailed { constructor (string memory name, string memory symbol, uint8 decimals, uint256 totalSupply) public ERC20Detailed (name, symbol, decimals) { _mint(msg.sender, totalSupply); } }
File 3 of 7: StateSender
/** Matic network contracts */ pragma solidity ^0.5.2; contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } contract StateSender is Ownable { using SafeMath for uint256; uint256 public counter; mapping(address => address) public registrations; event NewRegistration( address indexed user, address indexed sender, address indexed receiver ); event RegistrationUpdated( address indexed user, address indexed sender, address indexed receiver ); event StateSynced( uint256 indexed id, address indexed contractAddress, bytes data ); modifier onlyRegistered(address receiver) { require(registrations[receiver] == msg.sender, "Invalid sender"); _; } function syncState(address receiver, bytes calldata data) external onlyRegistered(receiver) { counter = counter.add(1); emit StateSynced(counter, receiver, data); } // register new contract for state sync function register(address sender, address receiver) public { require( isOwner() || registrations[receiver] == msg.sender, "StateSender.register: Not authorized to register" ); registrations[receiver] = sender; if (registrations[receiver] == address(0)) { emit NewRegistration(msg.sender, sender, receiver); } else { emit RegistrationUpdated(msg.sender, sender, receiver); } } }
File 4 of 7: DepositManager
// File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol pragma solidity ^0.5.2; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ contract IERC721Receiver { /** * @notice Handle the receipt of an NFT * @dev The ERC721 smart contract calls this function on the recipient * after a `safeTransfer`. This function MUST return the function selector, * otherwise the caller will revert the transaction. The selector to be * returned can be obtained as `this.onERC721Received.selector`. This * function MAY throw to revert and reject the transfer. * Note: the ERC721 contract address is always the message sender. * @param operator The address which called `safeTransferFrom` function * @param from The address which previously owned the token * @param tokenId The NFT identifier which is being transferred * @param data Additional data with no specified format * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` */ function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) public returns (bytes4); } // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Holder.sol pragma solidity ^0.5.2; contract ERC721Holder is IERC721Receiver { function onERC721Received(address, address, uint256, bytes memory) public returns (bytes4) { return this.onERC721Received.selector; } } // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.2; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // File: openzeppelin-solidity/contracts/introspection/IERC165.sol pragma solidity ^0.5.2; /** * @title IERC165 * @dev https://eips.ethereum.org/EIPS/eip-165 */ interface IERC165 { /** * @notice Query if a contract implements an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @dev Interface identification is specified in ERC-165. This function * uses less than 30,000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol pragma solidity ^0.5.2; /** * @title ERC721 Non-Fungible Token Standard basic interface * @dev see https://eips.ethereum.org/EIPS/eip-721 */ contract IERC721 is IERC165 { event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); function balanceOf(address owner) public view returns (uint256 balance); function ownerOf(uint256 tokenId) public view returns (address owner); function approve(address to, uint256 tokenId) public; function getApproved(uint256 tokenId) public view returns (address operator); function setApprovalForAll(address operator, bool _approved) public; function isApprovedForAll(address owner, address operator) public view returns (bool); function transferFrom(address from, address to, uint256 tokenId) public; function safeTransferFrom(address from, address to, uint256 tokenId) public; function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.2; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: openzeppelin-solidity/contracts/utils/Address.sol pragma solidity ^0.5.2; /** * Utility library of inline functions on addresses */ library Address { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param account address of the account to check * @return whether the target address is a contract */ function isContract(address account) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol pragma solidity ^0.5.2; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require((value == 0) || (token.allowance(address(this), spender) == 0)); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must equal true). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. require(address(token).isContract()); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool))); } } } // File: contracts/common/governance/IGovernance.sol pragma solidity ^0.5.2; interface IGovernance { function update(address target, bytes calldata data) external; } // File: contracts/common/governance/Governable.sol pragma solidity ^0.5.2; contract Governable { IGovernance public governance; constructor(address _governance) public { governance = IGovernance(_governance); } modifier onlyGovernance() { _assertGovernance(); _; } function _assertGovernance() private view { require( msg.sender == address(governance), "Only governance contract is authorized" ); } } // File: contracts/root/withdrawManager/IWithdrawManager.sol pragma solidity ^0.5.2; contract IWithdrawManager { function createExitQueue(address token) external; function verifyInclusion( bytes calldata data, uint8 offset, bool verifyTxInclusion ) external view returns (uint256 age); function addExitToQueue( address exitor, address childToken, address rootToken, uint256 exitAmountOrTokenId, bytes32 txHash, bool isRegularExit, uint256 priority ) external; function addInput( uint256 exitId, uint256 age, address utxoOwner, address token ) external; function challengeExit( uint256 exitId, uint256 inputId, bytes calldata challengeData, address adjudicatorPredicate ) external; } // File: contracts/common/Registry.sol pragma solidity ^0.5.2; contract Registry is Governable { // @todo hardcode constants bytes32 private constant WETH_TOKEN = keccak256("wethToken"); bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager"); bytes32 private constant STAKE_MANAGER = keccak256("stakeManager"); bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare"); bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager"); bytes32 private constant CHILD_CHAIN = keccak256("childChain"); bytes32 private constant STATE_SENDER = keccak256("stateSender"); bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager"); address public erc20Predicate; address public erc721Predicate; mapping(bytes32 => address) public contractMap; mapping(address => address) public rootToChildToken; mapping(address => address) public childToRootToken; mapping(address => bool) public proofValidatorContracts; mapping(address => bool) public isERC721; enum Type {Invalid, ERC20, ERC721, Custom} struct Predicate { Type _type; } mapping(address => Predicate) public predicates; event TokenMapped(address indexed rootToken, address indexed childToken); event ProofValidatorAdded(address indexed validator, address indexed from); event ProofValidatorRemoved(address indexed validator, address indexed from); event PredicateAdded(address indexed predicate, address indexed from); event PredicateRemoved(address indexed predicate, address indexed from); event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract); constructor(address _governance) public Governable(_governance) {} function updateContractMap(bytes32 _key, address _address) external onlyGovernance { emit ContractMapUpdated(_key, contractMap[_key], _address); contractMap[_key] = _address; } /** * @dev Map root token to child token * @param _rootToken Token address on the root chain * @param _childToken Token address on the child chain * @param _isERC721 Is the token being mapped ERC721 */ function mapToken( address _rootToken, address _childToken, bool _isERC721 ) external onlyGovernance { require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS"); rootToChildToken[_rootToken] = _childToken; childToRootToken[_childToken] = _rootToken; isERC721[_rootToken] = _isERC721; IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken); emit TokenMapped(_rootToken, _childToken); } function addErc20Predicate(address predicate) public onlyGovernance { require(predicate != address(0x0), "Can not add null address as predicate"); erc20Predicate = predicate; addPredicate(predicate, Type.ERC20); } function addErc721Predicate(address predicate) public onlyGovernance { erc721Predicate = predicate; addPredicate(predicate, Type.ERC721); } function addPredicate(address predicate, Type _type) public onlyGovernance { require(predicates[predicate]._type == Type.Invalid, "Predicate already added"); predicates[predicate]._type = _type; emit PredicateAdded(predicate, msg.sender); } function removePredicate(address predicate) public onlyGovernance { require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist"); delete predicates[predicate]; emit PredicateRemoved(predicate, msg.sender); } function getValidatorShareAddress() public view returns (address) { return contractMap[VALIDATOR_SHARE]; } function getWethTokenAddress() public view returns (address) { return contractMap[WETH_TOKEN]; } function getDepositManagerAddress() public view returns (address) { return contractMap[DEPOSIT_MANAGER]; } function getStakeManagerAddress() public view returns (address) { return contractMap[STAKE_MANAGER]; } function getSlashingManagerAddress() public view returns (address) { return contractMap[SLASHING_MANAGER]; } function getWithdrawManagerAddress() public view returns (address) { return contractMap[WITHDRAW_MANAGER]; } function getChildChainAndStateSender() public view returns (address, address) { return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]); } function isTokenMapped(address _token) public view returns (bool) { return rootToChildToken[_token] != address(0x0); } function isTokenMappedAndIsErc721(address _token) public view returns (bool) { require(isTokenMapped(_token), "TOKEN_NOT_MAPPED"); return isERC721[_token]; } function isTokenMappedAndGetPredicate(address _token) public view returns (address) { if (isTokenMappedAndIsErc721(_token)) { return erc721Predicate; } return erc20Predicate; } function isChildTokenErc721(address childToken) public view returns (bool) { address rootToken = childToRootToken[childToken]; require(rootToken != address(0x0), "Child token is not mapped"); return isERC721[rootToken]; } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol pragma solidity ^0.5.2; /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://eips.ethereum.org/EIPS/eip-20 * Originally based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for * all accounts just by listening to said events. Note that this isn't required by the specification, and other * compliant implementations may not do it. */ contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return A uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token to a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { _approve(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another. * Note that while this function emits an Approval event, this is not required as per the specification, * and other compliant implementations may not emit the event. * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 value) public returns (bool) { _transfer(from, to, value); _approve(from, msg.sender, _allowed[from][msg.sender].sub(value)); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when _allowed[msg.sender][spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue)); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when _allowed[msg.sender][spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue)); return true; } /** * @dev Transfer token for a specified addresses * @param from The address to transfer from. * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address from, address to, uint256 value) internal { require(to != address(0)); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param value The amount that will be created. */ function _mint(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.add(value); _balances[account] = _balances[account].add(value); emit Transfer(address(0), account, value); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @dev Approve an address to spend another addresses' tokens. * @param owner The address that owns the tokens. * @param spender The address that will spend the tokens. * @param value The number of tokens that can be spent. */ function _approve(address owner, address spender, uint256 value) internal { require(spender != address(0)); require(owner != address(0)); _allowed[owner][spender] = value; emit Approval(owner, spender, value); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * Emits an Approval event (reflecting the reduced allowance). * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { _burn(account, value); _approve(account, msg.sender, _allowed[account][msg.sender].sub(value)); } } // File: contracts/common/tokens/WETH.sol pragma solidity ^0.5.2; contract WETH is ERC20 { event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); function deposit() public payable; function withdraw(uint256 wad) public; function withdraw(uint256 wad, address user) public; } // File: contracts/root/depositManager/IDepositManager.sol pragma solidity ^0.5.2; interface IDepositManager { function depositEther() external payable; function transferAssets( address _token, address _user, uint256 _amountOrNFTId ) external; function depositERC20(address _token, uint256 _amount) external; function depositERC721(address _token, uint256 _tokenId) external; } // File: solidity-rlp/contracts/RLPReader.sol /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ pragma solidity ^0.5.0; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint len; uint memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint ptr = self.nextPtr; uint itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param item RLP encoded bytes */ function rlpLen(RLPItem memory item) internal pure returns (uint) { return item.len; } /* * @param item RLP encoded bytes */ function payloadLen(RLPItem memory item) internal pure returns (uint) { return item.len - _payloadOffset(item.memPtr); } /* * @param item RLP encoded list in bytes */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint memPtr = item.memPtr + _payloadOffset(item.memPtr); uint dataLen; for (uint i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint result; uint memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } return result == 0 ? false : true; } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(toUint(item)); } function toUint(RLPItem memory item) internal pure returns (uint) { require(item.len > 0 && item.len <= 33); uint offset = _payloadOffset(item.memPtr); uint len = item.len - offset; uint result; uint memPtr = item.memPtr + offset; assembly { result := mload(memPtr) // shfit to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint) { // one byte prefix require(item.len == 33); uint result; uint memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); uint offset = _payloadOffset(item.memPtr); uint len = item.len - offset; // data length bytes memory result = new bytes(len); uint destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint) { if (item.len == 0) return 0; uint count = 0; uint currPtr = item.memPtr + _payloadOffset(item.memPtr); uint endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint memPtr) private pure returns (uint) { uint itemLen; uint byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) itemLen = 1; else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint memPtr) private pure returns (uint) { uint byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint src, uint dest, uint len) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } // left over bytes. Mask is used to remove unwanted bytes from the word uint mask = 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } } // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/common/misc/ProxyStorage.sol pragma solidity ^0.5.2; contract ProxyStorage is Ownable { address internal proxyTo; } // File: contracts/common/mixin/ChainIdMixin.sol pragma solidity ^0.5.2; contract ChainIdMixin { bytes constant public networkId = hex"3A99"; uint256 constant public CHAINID = 15001; } // File: contracts/root/RootChainStorage.sol pragma solidity ^0.5.2; contract RootChainHeader { event NewHeaderBlock( address indexed proposer, uint256 indexed headerBlockId, uint256 indexed reward, uint256 start, uint256 end, bytes32 root ); // housekeeping event event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId); struct HeaderBlock { bytes32 root; uint256 start; uint256 end; uint256 createdAt; address proposer; } } contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin { bytes32 public heimdallId; uint8 public constant VOTE_TYPE = 2; uint16 internal constant MAX_DEPOSITS = 10000; uint256 public _nextHeaderBlock = MAX_DEPOSITS; uint256 internal _blockDepositId = 1; mapping(uint256 => HeaderBlock) public headerBlocks; Registry internal registry; } // File: contracts/staking/stakeManager/IStakeManager.sol pragma solidity 0.5.17; contract IStakeManager { // validator replacement function startAuction( uint256 validatorId, uint256 amount, bool acceptDelegation, bytes calldata signerPubkey ) external; function confirmAuctionBid(uint256 validatorId, uint256 heimdallFee) external; function transferFunds( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); function delegationDeposit( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); function unstake(uint256 validatorId) external; function totalStakedFor(address addr) external view returns (uint256); function stakeFor( address user, uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes memory signerPubkey ) public; function checkSignatures( uint256 blockInterval, bytes32 voteHash, bytes32 stateRoot, address proposer, uint[3][] calldata sigs ) external returns (uint256); function updateValidatorState(uint256 validatorId, int256 amount) public; function ownerOf(uint256 tokenId) public view returns (address); function slash(bytes calldata slashingInfoList) external returns (uint256); function validatorStake(uint256 validatorId) public view returns (uint256); function epoch() public view returns (uint256); function getRegistry() public view returns (address); function withdrawalDelay() public view returns (uint256); function delegatedAmount(uint256 validatorId) public view returns(uint256); function decreaseValidatorDelegatedAmount(uint256 validatorId, uint256 amount) public; function withdrawDelegatorsReward(uint256 validatorId) public returns(uint256); function delegatorsReward(uint256 validatorId) public view returns(uint256); function dethroneAndStake( address auctionUser, uint256 heimdallFee, uint256 validatorId, uint256 auctionAmount, bool acceptDelegation, bytes calldata signerPubkey ) external; } // File: contracts/root/IRootChain.sol pragma solidity ^0.5.2; interface IRootChain { function slash() external; function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external; function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external; function getLastChildBlock() external view returns (uint256); function currentHeaderBlock() external view returns (uint256); } // File: contracts/root/RootChain.sol pragma solidity ^0.5.2; contract RootChain is RootChainStorage, IRootChain { using SafeMath for uint256; using RLPReader for bytes; using RLPReader for RLPReader.RLPItem; modifier onlyDepositManager() { require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY"); _; } function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external { revert(); } function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external { (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256)); require(CHAINID == _borChainID, "Invalid bor chain id"); require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA"); // check if it is better to keep it in local storage instead IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress()); uint256 _reward = stakeManager.checkSignatures( end.sub(start).add(1), /** prefix 01 to data 01 represents positive vote on data and 00 is negative vote malicious validator can try to send 2/3 on negative vote so 01 is appended */ keccak256(abi.encodePacked(bytes(hex"01"), data)), accountHash, proposer, sigs ); require(_reward != 0, "Invalid checkpoint"); emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash); _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS); _blockDepositId = 1; } function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) { depositId = currentHeaderBlock().add(_blockDepositId); // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1) _blockDepositId = _blockDepositId.add(numDeposits); require( // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed _blockDepositId <= MAX_DEPOSITS, "TOO_MANY_DEPOSITS" ); } function getLastChildBlock() external view returns (uint256) { return headerBlocks[currentHeaderBlock()].end; } function slash() external { //TODO: future implementation } function currentHeaderBlock() public view returns (uint256) { return _nextHeaderBlock.sub(MAX_DEPOSITS); } function _buildHeaderBlock( address proposer, uint256 start, uint256 end, bytes32 rootHash ) private returns (bool) { uint256 nextChildBlock; /* The ID of the 1st header block is MAX_DEPOSITS. if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0 */ if (_nextHeaderBlock > MAX_DEPOSITS) { nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1; } if (nextChildBlock != start) { return false; } HeaderBlock memory headerBlock = HeaderBlock({ root: rootHash, start: nextChildBlock, end: end, createdAt: now, proposer: proposer }); headerBlocks[_nextHeaderBlock] = headerBlock; return true; } // Housekeeping function. @todo remove later function setNextHeaderBlock(uint256 _value) public onlyOwner { require(_value % MAX_DEPOSITS == 0, "Invalid value"); for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) { delete headerBlocks[i]; } _nextHeaderBlock = _value; _blockDepositId = 1; emit ResetHeaderBlock(msg.sender, _nextHeaderBlock); } // Housekeeping function. @todo remove later function setHeimdallId(string memory _heimdallId) public onlyOwner { heimdallId = keccak256(abi.encodePacked(_heimdallId)); } } // File: contracts/root/stateSyncer/StateSender.sol pragma solidity ^0.5.2; contract StateSender is Ownable { using SafeMath for uint256; uint256 public counter; mapping(address => address) public registrations; event NewRegistration( address indexed user, address indexed sender, address indexed receiver ); event RegistrationUpdated( address indexed user, address indexed sender, address indexed receiver ); event StateSynced( uint256 indexed id, address indexed contractAddress, bytes data ); modifier onlyRegistered(address receiver) { require(registrations[receiver] == msg.sender, "Invalid sender"); _; } function syncState(address receiver, bytes calldata data) external onlyRegistered(receiver) { counter = counter.add(1); emit StateSynced(counter, receiver, data); } // register new contract for state sync function register(address sender, address receiver) public { require( isOwner() || registrations[receiver] == msg.sender, "StateSender.register: Not authorized to register" ); registrations[receiver] = sender; if (registrations[receiver] == address(0)) { emit NewRegistration(msg.sender, sender, receiver); } else { emit RegistrationUpdated(msg.sender, sender, receiver); } } } // File: contracts/common/mixin/Lockable.sol pragma solidity ^0.5.2; contract Lockable { bool public locked; modifier onlyWhenUnlocked() { _assertUnlocked(); _; } function _assertUnlocked() private view { require(!locked, "locked"); } function lock() public { locked = true; } function unlock() public { locked = false; } } // File: contracts/common/mixin/GovernanceLockable.sol pragma solidity ^0.5.2; contract GovernanceLockable is Lockable, Governable { constructor(address governance) public Governable(governance) {} function lock() public onlyGovernance { super.lock(); } function unlock() public onlyGovernance { super.unlock(); } } // File: contracts/root/depositManager/DepositManagerStorage.sol pragma solidity ^0.5.2; contract DepositManagerHeader { event NewDepositBlock(address indexed owner, address indexed token, uint256 amountOrNFTId, uint256 depositBlockId); event MaxErc20DepositUpdate(uint256 indexed oldLimit, uint256 indexed newLimit); struct DepositBlock { bytes32 depositHash; uint256 createdAt; } } contract DepositManagerStorage is ProxyStorage, GovernanceLockable, DepositManagerHeader { Registry public registry; RootChain public rootChain; StateSender public stateSender; mapping(uint256 => DepositBlock) public deposits; address public childChain; uint256 public maxErc20Deposit = 100 * (10**18); } // File: contracts/root/depositManager/DepositManager.sol pragma solidity ^0.5.2; contract DepositManager is DepositManagerStorage, IDepositManager, ERC721Holder { using SafeMath for uint256; using SafeERC20 for IERC20; modifier isTokenMapped(address _token) { require(registry.isTokenMapped(_token), "TOKEN_NOT_SUPPORTED"); _; } modifier isPredicateAuthorized() { require(uint8(registry.predicates(msg.sender)) != 0, "Not a valid predicate"); _; } constructor() public GovernanceLockable(address(0x0)) {} // deposit ETH by sending to this contract function() external payable { depositEther(); } function updateMaxErc20Deposit(uint256 maxDepositAmount) public onlyGovernance { require(maxDepositAmount != 0); emit MaxErc20DepositUpdate(maxErc20Deposit, maxDepositAmount); maxErc20Deposit = maxDepositAmount; } function transferAssets( address _token, address _user, uint256 _amountOrNFTId ) external isPredicateAuthorized { address wethToken = registry.getWethTokenAddress(); if (registry.isERC721(_token)) { IERC721(_token).transferFrom(address(this), _user, _amountOrNFTId); } else if (_token == wethToken) { WETH t = WETH(_token); t.withdraw(_amountOrNFTId, _user); } else { require(IERC20(_token).transfer(_user, _amountOrNFTId), "TRANSFER_FAILED"); } } function depositERC20(address _token, uint256 _amount) external { depositERC20ForUser(_token, msg.sender, _amount); } function depositERC721(address _token, uint256 _tokenId) external { depositERC721ForUser(_token, msg.sender, _tokenId); } function depositBulk( address[] calldata _tokens, uint256[] calldata _amountOrTokens, address _user ) external onlyWhenUnlocked // unlike other deposit functions, depositBulk doesn't invoke _safeCreateDepositBlock { require(_tokens.length == _amountOrTokens.length, "Invalid Input"); uint256 depositId = rootChain.updateDepositId(_tokens.length); Registry _registry = registry; for (uint256 i = 0; i < _tokens.length; i++) { // will revert if token is not mapped if (_registry.isTokenMappedAndIsErc721(_tokens[i])) { _safeTransferERC721(msg.sender, _tokens[i], _amountOrTokens[i]); } else { IERC20(_tokens[i]).safeTransferFrom(msg.sender, address(this), _amountOrTokens[i]); } _createDepositBlock(_user, _tokens[i], _amountOrTokens[i], depositId); depositId = depositId.add(1); } } /** * @dev Caches childChain and stateSender (frequently used variables) from registry */ function updateChildChainAndStateSender() public { (address _childChain, address _stateSender) = registry.getChildChainAndStateSender(); require( _stateSender != address(stateSender) || _childChain != childChain, "Atleast one of stateSender or childChain address should change" ); childChain = _childChain; stateSender = StateSender(_stateSender); } function depositERC20ForUser( address _token, address _user, uint256 _amount ) public { require(_amount <= maxErc20Deposit, "exceed maximum deposit amount"); IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); _safeCreateDepositBlock(_user, _token, _amount); } function depositERC721ForUser( address _token, address _user, uint256 _tokenId ) public { require(registry.isTokenMappedAndIsErc721(_token), "not erc721"); _safeTransferERC721(msg.sender, _token, _tokenId); _safeCreateDepositBlock(_user, _token, _tokenId); } // @todo: write depositEtherForUser function depositEther() public payable { address wethToken = registry.getWethTokenAddress(); WETH t = WETH(wethToken); t.deposit.value(msg.value)(); _safeCreateDepositBlock(msg.sender, wethToken, msg.value); } function _safeCreateDepositBlock( address _user, address _token, uint256 _amountOrToken ) internal onlyWhenUnlocked isTokenMapped(_token) { _createDepositBlock( _user, _token, _amountOrToken, rootChain.updateDepositId(1) /* returns _depositId */ ); } function _createDepositBlock( address _user, address _token, uint256 _amountOrToken, uint256 _depositId ) internal { deposits[_depositId] = DepositBlock(keccak256(abi.encodePacked(_user, _token, _amountOrToken)), now); stateSender.syncState(childChain, abi.encode(_user, _token, _amountOrToken, _depositId)); emit NewDepositBlock(_user, _token, _amountOrToken, _depositId); } // Housekeeping function. @todo remove later function updateRootChain(address _rootChain) public onlyOwner { rootChain = RootChain(_rootChain); } function _safeTransferERC721(address _user, address _token, uint256 _tokenId) private { IERC721(_token).safeTransferFrom(_user, address(this), _tokenId); } }
File 5 of 7: Registry
/** Matic network contracts */ pragma solidity ^0.5.2; interface IGovernance { function update(address target, bytes calldata data) external; } contract Governable { IGovernance public governance; constructor(address _governance) public { governance = IGovernance(_governance); } modifier onlyGovernance() { require( msg.sender == address(governance), "Only governance contract is authorized" ); _; } } contract IWithdrawManager { function createExitQueue(address token) external; function verifyInclusion( bytes calldata data, uint8 offset, bool verifyTxInclusion ) external view returns (uint256 age); function addExitToQueue( address exitor, address childToken, address rootToken, uint256 exitAmountOrTokenId, bytes32 txHash, bool isRegularExit, uint256 priority ) external; function addInput( uint256 exitId, uint256 age, address utxoOwner, address token ) external; function challengeExit( uint256 exitId, uint256 inputId, bytes calldata challengeData, address adjudicatorPredicate ) external; } contract Registry is Governable { // @todo hardcode constants bytes32 private constant WETH_TOKEN = keccak256("wethToken"); bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager"); bytes32 private constant STAKE_MANAGER = keccak256("stakeManager"); bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare"); bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager"); bytes32 private constant CHILD_CHAIN = keccak256("childChain"); bytes32 private constant STATE_SENDER = keccak256("stateSender"); bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager"); address public erc20Predicate; address public erc721Predicate; mapping(bytes32 => address) public contractMap; mapping(address => address) public rootToChildToken; mapping(address => address) public childToRootToken; mapping(address => bool) public proofValidatorContracts; mapping(address => bool) public isERC721; enum Type {Invalid, ERC20, ERC721, Custom} struct Predicate { Type _type; } mapping(address => Predicate) public predicates; event TokenMapped(address indexed rootToken, address indexed childToken); event ProofValidatorAdded(address indexed validator, address indexed from); event ProofValidatorRemoved(address indexed validator, address indexed from); event PredicateAdded(address indexed predicate, address indexed from); event PredicateRemoved(address indexed predicate, address indexed from); event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract); constructor(address _governance) public Governable(_governance) {} function updateContractMap(bytes32 _key, address _address) external onlyGovernance { emit ContractMapUpdated(_key, contractMap[_key], _address); contractMap[_key] = _address; } /** * @dev Map root token to child token * @param _rootToken Token address on the root chain * @param _childToken Token address on the child chain * @param _isERC721 Is the token being mapped ERC721 */ function mapToken( address _rootToken, address _childToken, bool _isERC721 ) external onlyGovernance { require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS"); rootToChildToken[_rootToken] = _childToken; childToRootToken[_childToken] = _rootToken; isERC721[_rootToken] = _isERC721; IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken); emit TokenMapped(_rootToken, _childToken); } function addErc20Predicate(address predicate) public onlyGovernance { require(predicate != address(0x0), "Can not add null address as predicate"); erc20Predicate = predicate; addPredicate(predicate, Type.ERC20); } function addErc721Predicate(address predicate) public onlyGovernance { erc721Predicate = predicate; addPredicate(predicate, Type.ERC721); } function addPredicate(address predicate, Type _type) public onlyGovernance { require(predicates[predicate]._type == Type.Invalid, "Predicate already added"); predicates[predicate]._type = _type; emit PredicateAdded(predicate, msg.sender); } function removePredicate(address predicate) public onlyGovernance { require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist"); delete predicates[predicate]; emit PredicateRemoved(predicate, msg.sender); } function getValidatorShareAddress() public view returns (address) { return contractMap[VALIDATOR_SHARE]; } function getWethTokenAddress() public view returns (address) { return contractMap[WETH_TOKEN]; } function getDepositManagerAddress() public view returns (address) { return contractMap[DEPOSIT_MANAGER]; } function getStakeManagerAddress() public view returns (address) { return contractMap[STAKE_MANAGER]; } function getSlashingManagerAddress() public view returns (address) { return contractMap[SLASHING_MANAGER]; } function getWithdrawManagerAddress() public view returns (address) { return contractMap[WITHDRAW_MANAGER]; } function getChildChainAndStateSender() public view returns (address, address) { return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]); } function isTokenMapped(address _token) public view returns (bool) { return rootToChildToken[_token] != address(0x0); } function isTokenMappedAndIsErc721(address _token) public view returns (bool) { require(isTokenMapped(_token), "TOKEN_NOT_MAPPED"); return isERC721[_token]; } function isTokenMappedAndGetPredicate(address _token) public view returns (address) { if (isTokenMappedAndIsErc721(_token)) { return erc721Predicate; } return erc20Predicate; } function isChildTokenErc721(address childToken) public view returns (bool) { address rootToken = childToRootToken[childToken]; require(rootToken != address(0x0), "Child token is not mapped"); return isERC721[rootToken]; } }
File 6 of 7: RootChainProxy
// File: contracts/common/governance/IGovernance.sol pragma solidity ^0.5.2; interface IGovernance { function update(address target, bytes calldata data) external; } // File: contracts/common/governance/Governable.sol pragma solidity ^0.5.2; contract Governable { IGovernance public governance; constructor(address _governance) public { governance = IGovernance(_governance); } modifier onlyGovernance() { require(msg.sender == address(governance), "Only governance contract is authorized"); _; } } // File: contracts/root/withdrawManager/IWithdrawManager.sol pragma solidity ^0.5.2; contract IWithdrawManager { function createExitQueue(address token) external; function verifyInclusion( bytes calldata data, uint8 offset, bool verifyTxInclusion ) external view returns (uint256 age); function addExitToQueue( address exitor, address childToken, address rootToken, uint256 exitAmountOrTokenId, bytes32 txHash, bool isRegularExit, uint256 priority ) external; function addInput( uint256 exitId, uint256 age, address utxoOwner, address token ) external; function challengeExit( uint256 exitId, uint256 inputId, bytes calldata challengeData, address adjudicatorPredicate ) external; } // File: contracts/common/Registry.sol pragma solidity ^0.5.2; contract Registry is Governable { // @todo hardcode constants bytes32 private constant WETH_TOKEN = keccak256("wethToken"); bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager"); bytes32 private constant STAKE_MANAGER = keccak256("stakeManager"); bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare"); bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager"); bytes32 private constant CHILD_CHAIN = keccak256("childChain"); bytes32 private constant STATE_SENDER = keccak256("stateSender"); bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager"); address public erc20Predicate; address public erc721Predicate; mapping(bytes32 => address) public contractMap; mapping(address => address) public rootToChildToken; mapping(address => address) public childToRootToken; mapping(address => bool) public proofValidatorContracts; mapping(address => bool) public isERC721; enum Type {Invalid, ERC20, ERC721, Custom} struct Predicate { Type _type; } mapping(address => Predicate) public predicates; event TokenMapped(address indexed rootToken, address indexed childToken); event ProofValidatorAdded(address indexed validator, address indexed from); event ProofValidatorRemoved(address indexed validator, address indexed from); event PredicateAdded(address indexed predicate, address indexed from); event PredicateRemoved(address indexed predicate, address indexed from); event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract); constructor(address _governance) public Governable(_governance) {} function updateContractMap(bytes32 _key, address _address) external onlyGovernance { emit ContractMapUpdated(_key, contractMap[_key], _address); contractMap[_key] = _address; } /** * @dev Map root token to child token * @param _rootToken Token address on the root chain * @param _childToken Token address on the child chain * @param _isERC721 Is the token being mapped ERC721 */ function mapToken( address _rootToken, address _childToken, bool _isERC721 ) external onlyGovernance { require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS"); rootToChildToken[_rootToken] = _childToken; childToRootToken[_childToken] = _rootToken; isERC721[_rootToken] = _isERC721; IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken); emit TokenMapped(_rootToken, _childToken); } function addErc20Predicate(address predicate) public onlyGovernance { require(predicate != address(0x0), "Can not add null address as predicate"); erc20Predicate = predicate; addPredicate(predicate, Type.ERC20); } function addErc721Predicate(address predicate) public onlyGovernance { erc721Predicate = predicate; addPredicate(predicate, Type.ERC721); } function addPredicate(address predicate, Type _type) public onlyGovernance { require(predicates[predicate]._type == Type.Invalid, "Predicate already added"); predicates[predicate]._type = _type; emit PredicateAdded(predicate, msg.sender); } function removePredicate(address predicate) public onlyGovernance { require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist"); delete predicates[predicate]; emit PredicateRemoved(predicate, msg.sender); } function getValidatorShareAddress() public view returns (address) { return contractMap[VALIDATOR_SHARE]; } function getWethTokenAddress() public view returns (address) { return contractMap[WETH_TOKEN]; } function getDepositManagerAddress() public view returns (address) { return contractMap[DEPOSIT_MANAGER]; } function getStakeManagerAddress() public view returns (address) { return contractMap[STAKE_MANAGER]; } function getSlashingManagerAddress() public view returns (address) { return contractMap[SLASHING_MANAGER]; } function getWithdrawManagerAddress() public view returns (address) { return contractMap[WITHDRAW_MANAGER]; } function getChildChainAndStateSender() public view returns (address, address) { return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]); } function isTokenMapped(address _token) public view returns (bool) { return rootToChildToken[_token] != address(0x0); } function isTokenMappedAndIsErc721(address _token) public view returns (bool) { require(isTokenMapped(_token), "TOKEN_NOT_MAPPED"); return isERC721[_token]; } function isTokenMappedAndGetPredicate(address _token) public view returns (address) { if (isTokenMappedAndIsErc721(_token)) { return erc721Predicate; } return erc20Predicate; } function isChildTokenErc721(address childToken) public view returns (bool) { address rootToken = childToRootToken[childToken]; require(rootToken != address(0x0), "Child token is not mapped"); return isERC721[rootToken]; } } // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/common/misc/ProxyStorage.sol pragma solidity ^0.5.2; contract ProxyStorage is Ownable { address internal proxyTo; } // File: contracts/common/mixin/ChainIdMixin.sol pragma solidity ^0.5.2; contract ChainIdMixin { bytes public constant networkId = hex"89"; uint256 public constant CHAINID = 137; } // File: contracts/root/RootChainStorage.sol pragma solidity ^0.5.2; contract RootChainHeader { event NewHeaderBlock( address indexed proposer, uint256 indexed headerBlockId, uint256 indexed reward, uint256 start, uint256 end, bytes32 root ); // housekeeping event event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId); struct HeaderBlock { bytes32 root; uint256 start; uint256 end; uint256 createdAt; address proposer; } } contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin { bytes32 public heimdallId; uint8 public constant VOTE_TYPE = 2; uint16 internal constant MAX_DEPOSITS = 10000; uint256 public _nextHeaderBlock = MAX_DEPOSITS; uint256 internal _blockDepositId = 1; mapping(uint256 => HeaderBlock) public headerBlocks; Registry internal registry; } // File: contracts/common/misc/ERCProxy.sol /* * SPDX-License-Identitifer: MIT */ pragma solidity ^0.5.2; // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md interface ERCProxy { function proxyType() external pure returns (uint256 proxyTypeId); function implementation() external view returns (address codeAddr); } // File: contracts/common/misc/DelegateProxy.sol pragma solidity ^0.5.2; contract DelegateProxy is ERCProxy { function proxyType() external pure returns (uint256 proxyTypeId) { // Upgradeable proxy proxyTypeId = 2; } function implementation() external view returns (address); function delegatedFwd(address _dst, bytes memory _calldata) internal { // solium-disable-next-line security/no-inline-assembly assembly { let result := delegatecall(sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) let size := returndatasize let ptr := mload(0x40) returndatacopy(ptr, 0, size) // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. // if the call returned error data, forward it switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } } // File: contracts/common/misc/Proxy.sol pragma solidity ^0.5.2; contract Proxy is ProxyStorage, DelegateProxy { event ProxyUpdated(address indexed _new, address indexed _old); event OwnerUpdate(address _prevOwner, address _newOwner); constructor(address _proxyTo) public { updateImplementation(_proxyTo); } function() external payable { // require(currentContract != 0, "If app code has not been set yet, do not call"); // Todo: filter out some calls or handle in the end fallback delegatedFwd(proxyTo, msg.data); } function implementation() external view returns (address) { return proxyTo; } function updateImplementation(address _newProxyTo) public onlyOwner { require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS"); require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT"); emit ProxyUpdated(_newProxyTo, proxyTo); proxyTo = _newProxyTo; } function isContract(address _target) internal view returns (bool) { if (_target == address(0)) { return false; } uint256 size; assembly { size := extcodesize(_target) } return size > 0; } } // File: contracts/root/RootChainProxy.sol pragma solidity ^0.5.2; contract RootChainProxy is Proxy, RootChainStorage { constructor( address _proxyTo, address _registry, string memory _heimdallId ) public Proxy(_proxyTo) { registry = Registry(_registry); heimdallId = keccak256(abi.encodePacked(_heimdallId)); } }
File 7 of 7: RootChain
// File: solidity-rlp/contracts/RLPReader.sol // SPDX-License-Identifier: Apache-2.0 /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ pragma solidity >=0.5.0 <0.7.0; library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint len; uint memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint ptr = self.nextPtr; uint itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param item RLP encoded bytes */ function rlpLen(RLPItem memory item) internal pure returns (uint) { return item.len; } /* * @param item RLP encoded bytes */ function payloadLen(RLPItem memory item) internal pure returns (uint) { return item.len - _payloadOffset(item.memPtr); } /* * @param item RLP encoded list in bytes */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint memPtr = item.memPtr + _payloadOffset(item.memPtr); uint dataLen; for (uint i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte except "0x80" is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint result; uint memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } // SEE Github Issue #5. // Summary: Most commonly used RLP libraries (i.e Geth) will encode // "0" as "0x80" instead of as "0". We handle this edge case explicitly // here. if (result == 0 || result == STRING_SHORT_START) { return false; } else { return true; } } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(toUint(item)); } function toUint(RLPItem memory item) internal pure returns (uint) { require(item.len > 0 && item.len <= 33); uint offset = _payloadOffset(item.memPtr); uint len = item.len - offset; uint result; uint memPtr = item.memPtr + offset; assembly { result := mload(memPtr) // shfit to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint) { // one byte prefix require(item.len == 33); uint result; uint memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); uint offset = _payloadOffset(item.memPtr); uint len = item.len - offset; // data length bytes memory result = new bytes(len); uint destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint) { if (item.len == 0) return 0; uint count = 0; uint currPtr = item.memPtr + _payloadOffset(item.memPtr); uint endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint memPtr) private pure returns (uint) { uint itemLen; uint byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) itemLen = 1; else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint memPtr) private pure returns (uint) { uint byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy(uint src, uint dest, uint len) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } // left over bytes. Mask is used to remove unwanted bytes from the word uint mask = 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.2; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: contracts/common/governance/IGovernance.sol pragma solidity ^0.5.2; interface IGovernance { function update(address target, bytes calldata data) external; } // File: contracts/common/governance/Governable.sol pragma solidity ^0.5.2; contract Governable { IGovernance public governance; constructor(address _governance) public { governance = IGovernance(_governance); } modifier onlyGovernance() { _assertGovernance(); _; } function _assertGovernance() private view { require( msg.sender == address(governance), "Only governance contract is authorized" ); } } // File: contracts/root/withdrawManager/IWithdrawManager.sol pragma solidity ^0.5.2; contract IWithdrawManager { function createExitQueue(address token) external; function verifyInclusion( bytes calldata data, uint8 offset, bool verifyTxInclusion ) external view returns (uint256 age); function addExitToQueue( address exitor, address childToken, address rootToken, uint256 exitAmountOrTokenId, bytes32 txHash, bool isRegularExit, uint256 priority ) external; function addInput( uint256 exitId, uint256 age, address utxoOwner, address token ) external; function challengeExit( uint256 exitId, uint256 inputId, bytes calldata challengeData, address adjudicatorPredicate ) external; } // File: contracts/common/Registry.sol pragma solidity ^0.5.2; contract Registry is Governable { // @todo hardcode constants bytes32 private constant WETH_TOKEN = keccak256("wethToken"); bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager"); bytes32 private constant STAKE_MANAGER = keccak256("stakeManager"); bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare"); bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager"); bytes32 private constant CHILD_CHAIN = keccak256("childChain"); bytes32 private constant STATE_SENDER = keccak256("stateSender"); bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager"); address public erc20Predicate; address public erc721Predicate; mapping(bytes32 => address) public contractMap; mapping(address => address) public rootToChildToken; mapping(address => address) public childToRootToken; mapping(address => bool) public proofValidatorContracts; mapping(address => bool) public isERC721; enum Type {Invalid, ERC20, ERC721, Custom} struct Predicate { Type _type; } mapping(address => Predicate) public predicates; event TokenMapped(address indexed rootToken, address indexed childToken); event ProofValidatorAdded(address indexed validator, address indexed from); event ProofValidatorRemoved(address indexed validator, address indexed from); event PredicateAdded(address indexed predicate, address indexed from); event PredicateRemoved(address indexed predicate, address indexed from); event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract); constructor(address _governance) public Governable(_governance) {} function updateContractMap(bytes32 _key, address _address) external onlyGovernance { emit ContractMapUpdated(_key, contractMap[_key], _address); contractMap[_key] = _address; } /** * @dev Map root token to child token * @param _rootToken Token address on the root chain * @param _childToken Token address on the child chain * @param _isERC721 Is the token being mapped ERC721 */ function mapToken( address _rootToken, address _childToken, bool _isERC721 ) external onlyGovernance { require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS"); rootToChildToken[_rootToken] = _childToken; childToRootToken[_childToken] = _rootToken; isERC721[_rootToken] = _isERC721; IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken); emit TokenMapped(_rootToken, _childToken); } function addErc20Predicate(address predicate) public onlyGovernance { require(predicate != address(0x0), "Can not add null address as predicate"); erc20Predicate = predicate; addPredicate(predicate, Type.ERC20); } function addErc721Predicate(address predicate) public onlyGovernance { erc721Predicate = predicate; addPredicate(predicate, Type.ERC721); } function addPredicate(address predicate, Type _type) public onlyGovernance { require(predicates[predicate]._type == Type.Invalid, "Predicate already added"); predicates[predicate]._type = _type; emit PredicateAdded(predicate, msg.sender); } function removePredicate(address predicate) public onlyGovernance { require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist"); delete predicates[predicate]; emit PredicateRemoved(predicate, msg.sender); } function getValidatorShareAddress() public view returns (address) { return contractMap[VALIDATOR_SHARE]; } function getWethTokenAddress() public view returns (address) { return contractMap[WETH_TOKEN]; } function getDepositManagerAddress() public view returns (address) { return contractMap[DEPOSIT_MANAGER]; } function getStakeManagerAddress() public view returns (address) { return contractMap[STAKE_MANAGER]; } function getSlashingManagerAddress() public view returns (address) { return contractMap[SLASHING_MANAGER]; } function getWithdrawManagerAddress() public view returns (address) { return contractMap[WITHDRAW_MANAGER]; } function getChildChainAndStateSender() public view returns (address, address) { return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]); } function isTokenMapped(address _token) public view returns (bool) { return rootToChildToken[_token] != address(0x0); } function isTokenMappedAndIsErc721(address _token) public view returns (bool) { require(isTokenMapped(_token), "TOKEN_NOT_MAPPED"); return isERC721[_token]; } function isTokenMappedAndGetPredicate(address _token) public view returns (address) { if (isTokenMappedAndIsErc721(_token)) { return erc721Predicate; } return erc20Predicate; } function isChildTokenErc721(address childToken) public view returns (bool) { address rootToken = childToRootToken[childToken]; require(rootToken != address(0x0), "Child token is not mapped"); return isERC721[rootToken]; } } // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/common/misc/ProxyStorage.sol pragma solidity ^0.5.2; contract ProxyStorage is Ownable { address internal proxyTo; } // File: contracts/common/mixin/ChainIdMixin.sol pragma solidity ^0.5.2; contract ChainIdMixin { bytes constant public networkId = hex"89"; uint256 constant public CHAINID = 137; } // File: contracts/root/RootChainStorage.sol pragma solidity ^0.5.2; contract RootChainHeader { event NewHeaderBlock( address indexed proposer, uint256 indexed headerBlockId, uint256 indexed reward, uint256 start, uint256 end, bytes32 root ); // housekeeping event event ResetHeaderBlock(address indexed proposer, uint256 indexed headerBlockId); struct HeaderBlock { bytes32 root; uint256 start; uint256 end; uint256 createdAt; address proposer; } } contract RootChainStorage is ProxyStorage, RootChainHeader, ChainIdMixin { bytes32 public heimdallId; uint8 public constant VOTE_TYPE = 2; uint16 internal constant MAX_DEPOSITS = 10000; uint256 public _nextHeaderBlock = MAX_DEPOSITS; uint256 internal _blockDepositId = 1; mapping(uint256 => HeaderBlock) public headerBlocks; Registry internal registry; } // File: contracts/staking/stakeManager/IStakeManager.sol pragma solidity 0.5.17; contract IStakeManager { // validator replacement function startAuction( uint256 validatorId, uint256 amount, bool acceptDelegation, bytes calldata signerPubkey ) external; function confirmAuctionBid(uint256 validatorId, uint256 heimdallFee) external; function transferFunds( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); function delegationDeposit( uint256 validatorId, uint256 amount, address delegator ) external returns (bool); function unstake(uint256 validatorId) external; function totalStakedFor(address addr) external view returns (uint256); function stakeFor( address user, uint256 amount, uint256 heimdallFee, bool acceptDelegation, bytes memory signerPubkey ) public; function checkSignatures( uint256 blockInterval, bytes32 voteHash, bytes32 stateRoot, address proposer, uint[3][] calldata sigs ) external returns (uint256); function updateValidatorState(uint256 validatorId, int256 amount) public; function ownerOf(uint256 tokenId) public view returns (address); function slash(bytes calldata slashingInfoList) external returns (uint256); function validatorStake(uint256 validatorId) public view returns (uint256); function epoch() public view returns (uint256); function getRegistry() public view returns (address); function withdrawalDelay() public view returns (uint256); function delegatedAmount(uint256 validatorId) public view returns(uint256); function decreaseValidatorDelegatedAmount(uint256 validatorId, uint256 amount) public; function withdrawDelegatorsReward(uint256 validatorId) public returns(uint256); function delegatorsReward(uint256 validatorId) public view returns(uint256); function dethroneAndStake( address auctionUser, uint256 heimdallFee, uint256 validatorId, uint256 auctionAmount, bool acceptDelegation, bytes calldata signerPubkey ) external; } // File: contracts/root/IRootChain.sol pragma solidity ^0.5.2; interface IRootChain { function slash() external; function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external; function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external; function getLastChildBlock() external view returns (uint256); function currentHeaderBlock() external view returns (uint256); } // File: contracts/root/RootChain.sol pragma solidity ^0.5.2; contract RootChain is RootChainStorage, IRootChain { using SafeMath for uint256; using RLPReader for bytes; using RLPReader for RLPReader.RLPItem; modifier onlyDepositManager() { require(msg.sender == registry.getDepositManagerAddress(), "UNAUTHORIZED_DEPOSIT_MANAGER_ONLY"); _; } function submitHeaderBlock(bytes calldata data, bytes calldata sigs) external { revert(); } function submitCheckpoint(bytes calldata data, uint[3][] calldata sigs) external { (address proposer, uint256 start, uint256 end, bytes32 rootHash, bytes32 accountHash, uint256 _borChainID) = abi .decode(data, (address, uint256, uint256, bytes32, bytes32, uint256)); require(CHAINID == _borChainID, "Invalid bor chain id"); require(_buildHeaderBlock(proposer, start, end, rootHash), "INCORRECT_HEADER_DATA"); // check if it is better to keep it in local storage instead IStakeManager stakeManager = IStakeManager(registry.getStakeManagerAddress()); uint256 _reward = stakeManager.checkSignatures( end.sub(start).add(1), /** prefix 01 to data 01 represents positive vote on data and 00 is negative vote malicious validator can try to send 2/3 on negative vote so 01 is appended */ keccak256(abi.encodePacked(bytes(hex"01"), data)), accountHash, proposer, sigs ); require(_reward != 0, "Invalid checkpoint"); emit NewHeaderBlock(proposer, _nextHeaderBlock, _reward, start, end, rootHash); _nextHeaderBlock = _nextHeaderBlock.add(MAX_DEPOSITS); _blockDepositId = 1; } function updateDepositId(uint256 numDeposits) external onlyDepositManager returns (uint256 depositId) { depositId = currentHeaderBlock().add(_blockDepositId); // deposit ids will be (_blockDepositId, _blockDepositId + 1, .... _blockDepositId + numDeposits - 1) _blockDepositId = _blockDepositId.add(numDeposits); require( // Since _blockDepositId is initialized to 1; only (MAX_DEPOSITS - 1) deposits per header block are allowed _blockDepositId <= MAX_DEPOSITS, "TOO_MANY_DEPOSITS" ); } function getLastChildBlock() external view returns (uint256) { return headerBlocks[currentHeaderBlock()].end; } function slash() external { //TODO: future implementation } function currentHeaderBlock() public view returns (uint256) { return _nextHeaderBlock.sub(MAX_DEPOSITS); } function _buildHeaderBlock( address proposer, uint256 start, uint256 end, bytes32 rootHash ) private returns (bool) { uint256 nextChildBlock; /* The ID of the 1st header block is MAX_DEPOSITS. if _nextHeaderBlock == MAX_DEPOSITS, then the first header block is yet to be submitted, hence nextChildBlock = 0 */ if (_nextHeaderBlock > MAX_DEPOSITS) { nextChildBlock = headerBlocks[currentHeaderBlock()].end + 1; } if (nextChildBlock != start) { return false; } HeaderBlock memory headerBlock = HeaderBlock({ root: rootHash, start: nextChildBlock, end: end, createdAt: now, proposer: proposer }); headerBlocks[_nextHeaderBlock] = headerBlock; return true; } // Housekeeping function. @todo remove later function setNextHeaderBlock(uint256 _value) public onlyOwner { require(_value % MAX_DEPOSITS == 0, "Invalid value"); for (uint256 i = _value; i < _nextHeaderBlock; i += MAX_DEPOSITS) { delete headerBlocks[i]; } _nextHeaderBlock = _value; _blockDepositId = 1; emit ResetHeaderBlock(msg.sender, _nextHeaderBlock); } // Housekeeping function. @todo remove later function setHeimdallId(string memory _heimdallId) public onlyOwner { heimdallId = keccak256(abi.encodePacked(_heimdallId)); } }