Transaction Hash:
Block:
19114656 at Jan-29-2024 08:46:59 PM +UTC
Transaction Fee:
0.003332222560379048 ETH
$6.95
Gas Used:
227,416 Gas / 14.652542303 Gwei
Emitted Events:
| 53 |
EtherPredicateProxy.0x0fc0eed41f72d3da77d0f53b9594fc7073acd15ee9d7c536819a70a67c57ef3c( 0x0fc0eed41f72d3da77d0f53b9594fc7073acd15ee9d7c536819a70a67c57ef3c, 0x000000000000000000000000b9de8c12caf8547d79b4c8e7cc6a77eff994af4c, 0000000000000000000000000000000000000000000000003b820947fce00000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 8.194677411557542247 Eth | 8.195121965653115023 Eth | 0.000444554095572776 | |
| 0x8484Ef72...E6e922B30 | (Polygon (Matic): Ether Bridge) | 180,844.829728305576955584 Eth | 180,840.541728305576955584 Eth | 4.288 | |
| 0xA0c68C63...1bFc77C77 | (Polygon (Matic): Bridge) | ||||
| 0xb9DE8C12...FF994Af4C |
5.758575462487143976 Eth
Nonce: 78
|
10.043243239926764928 Eth
Nonce: 79
| 4.284667777439620952 |
Execution Trace
RootChainManagerProxy.3805550f( )
RootChainManager.exit( inputData=0xF90AEA8422076530B90160B3B88603CE09628330A8B07D1D4DFB4FA76E9037E23A0790D9EE6425DA3DB3F40FC80CCC94124BC441802597B479133F3A0F7C241773A7FB6293E62B267502CA4F47DD82EDAA625A06C041BB452D47B4F9F5E5E5C07B4E9BF8A50571AEB37F5BA19DA4E83D2E8FFB9C82FA204BB614239F78EAD1F12AD22F51372547F53166345097713FE732344E0A0A115459165D9FAC189039E18CB4F09433CC1A74864E7D06A7333D9CAEA2EA2204A2B0178DBF8B9EB97F92B190A06048CC17F04068AFD1A77ABFF542D2E719142F8C0E4A381387330DC6EB76F73110CA5550793AC3668DF6D460C933AAB12F8B89C55B2658EA9A721C819A5AD681EC5BFABF7F274F368832645D583AD5DE027977074A94E9EF6A458EBD5D10A286EF4CE3D8C2578222D2383F300332A30B027CD931CEA79B0990288320E571A7864CBE6281D47840597658F3E1DA98EA80B6167FA48D3952AE2E34D91B186FE99F290AFD2C52995A737E84032720D98465B7F555A0EC106E125C3E52EAEBCA6112481F1498A610C8F5A55853CDB22F98001F843F98A0FCA3E1D15EAF8F41B540E16C9F98534EE2FF2E51D2A0C7BC97D6CEDFC8983272B902ED02F902E90184019C47D1B9010000000000000000000000000000000000000000000000000010000004000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000008000000800000000000000000000100000000000000000040020000000000000000000800000000000000000080000010000000000000000000000000000000000000000000010000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004000000002000000000001000000000000000000000000000000100000000020000000000000000000000000044000000080000000000000000000000000100000F901DDF89B947CEB23FD6BC0ADD59E62AC25578270CFF1B9F619F863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA0000000000000000000000000B9DE8C12CAF8547D79B4C8E7CC6A77EFF994AF4CA00000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000003B820947FCE00000F9013D940000000000000000000000000000000000001010F884A04DFE1BBBCF077DDC3E01291EEA2D5C70C2B422B415D95645B9ADCFD678CB1D63A00000000000000000000000000000000000000000000000000000000000001010A0000000000000000000000000B9DE8C12CAF8547D79B4C8E7CC6A77EFF994AF4CA00000000000000000000000001D25C827ABD466387BDA00B429FE728627D6EEE6B8A0000000000000000000000000000000000000000000000000000342902B834E56000000000000000000000000000000000000000000000089BDDA357B9906D49900000000000000000000000000000000000000000000012A518D1B0EC5C5C2C1000000000000000000000000000000000000000000000089BDD6F2EB6D83864300000000000000000000000000000000000000000000012A51905D9EF1491117B9063FF9063CF90131A0E5C451E640B67DE698F5B7854253C76B935EF72B2FFEC5D89E068F73CA72D1B9A0E3F5E88D3BB1F1F73F0415DD72BC284098AA1435DA0429644B539DDD68BC2390A0AAE6893DF4E273D835CDAE3E307EC53397CA56894776704D449364D0E2887FFAA0773DD6D8847818F2D9D9BACC0BEF8DD70B158CAAF35B3965791EA4F9EE15165BA072167A763D5C491133FB2AA5B842A2B5F261E68AFD6ECD5270E0ED13698EA44AA09F2D6B0626BABD79ECD34AB7E1BBD5DB0228C1F7D870531A4E8C60860040B091A068DAA868A44A578AA8C161FFC5CE430ECCC8AF87A006EEF647EF3A91B79CF98AA0EB611DFF2FAEC4CA5CF657489D5F481503C7F2044FFEF85D0FD845AFCA6D7DEFA0F2F3995A83A97051DE3E3F58E5FDE19C4BE57596B4803EA3F7511B8D8B3BFFC58080808080808080F90211A0AD75A38A41E119EC5EBFDE7463DF41D8E53F4AA13C1C209021A2B0DCE9B9939EA026312E239EA268DF50669FA2CDE4928377CD296619DE9144F1E9F6DF13F18948A0151BB581B983AC64B5E50D6ED6127899E42DBCD896B9E2B923E3BB7295DA3FC7A088D1AE16CED39AD835350595A7DC2E0B8934AE18A8D59991105345C6E78AFA25A017B8174CF17CFFFBF9EC63B78AC019BAC46440BF64AB45C9550C6B76043D25BFA0D2FEEB6ABF7663333A382942494011149ADEC06F3FBAF116A1E63B27F53857F4A0D28844C7C2C5F42D7E128002AA8ADB7BDEB45DA4DA0E28EBE1715F2364D44455A077E352A30403A46EEE251A9DC8B87246029C982FD91DDE03BB22D6D010332D6AA077A9806FF6A2CC12A691A857AE18805CB5B2F713B2A419321130A113AC4E0C98A0DE2D37B02EAD88DAFBCFA92E66E8F3DD7504988172AED28E44DB4EF31B0ECF3FA0F86A419D113149F7F499B77B7674A4726105C9683A4F5EA9A032FB16DB23063DA0D6AF4C073ABA7175CB9DA1740B5AF66A01DF51AC2DA76F8065426638A25A58B8A0322AF71DFC8CC66C6907E5CDFE37AC0513DDF7BF89CF332EC30EA7D102201E92A0CFD8B4C55D7B676C1707239A28765841453768D9FFE2FCFE86A8AD88EF631DC5A08787474F995C0EA6CDC12B963697B65102F92A03FB53FF9D652E7285451D83B2A079B8D9C73F2EC95D5E2B6E072000CE65C8EDC8B3533D6DEC49B0B6FC3D17734780F902F120B902ED02F902E90184019C47D1B9010000000000000000000000000000000000000000000000000010000004000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000008000000800000000000000000000100000000000000000040020000000000000000000800000000000000000080000010000000000000000000000000000000000000000000010000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004000000002000000000001000000000000000000000000000000100000000020000000000000000000000000044000000080000000000000000000000000100000F901DDF89B947CEB23FD6BC0ADD59E62AC25578270CFF1B9F619F863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA0000000000000000000000000B9DE8C12CAF8547D79B4C8E7CC6A77EFF994AF4CA00000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000003B820947FCE00000F9013D940000000000000000000000000000000000001010F884A04DFE1BBBCF077DDC3E01291EEA2D5C70C2B422B415D95645B9ADCFD678CB1D63A00000000000000000000000000000000000000000000000000000000000001010A0000000000000000000000000B9DE8C12CAF8547D79B4C8E7CC6A77EFF994AF4CA00000000000000000000000001D25C827ABD466387BDA00B429FE728627D6EEE6B8A0000000000000000000000000000000000000000000000000000342902B834E56000000000000000000000000000000000000000000000089BDDA357B9906D49900000000000000000000000000000000000000000000012A518D1B0EC5C5C2C1000000000000000000000000000000000000000000000089BDD6F2EB6D83864300000000000000000000000000000000000000000000012A51905D9EF149111782007380 )-
RootChainProxy.headerBlocks( 570910000 ) => ( root=993F2FBEE50A4DF2592EA74E9660185F7F5D0F9166EDD7C3BF7FDE9D8D21ABE7, start=52895910, end=52897701, createdAt=1706560391, proposer=0x67B94473D81D0cd00849D563C94d0432Ac988B49 ) EtherPredicateProxy.8274664f( )EtherPredicate.exitTokens( 0xb9DE8C12CAf8547d79b4c8e7Cc6A77eFF994Af4C, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, log=0xF89B947CEB23FD6BC0ADD59E62AC25578270CFF1B9F619F863A0DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EFA0000000000000000000000000B9DE8C12CAF8547D79B4C8E7CC6A77EFF994AF4CA00000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000003B820947FCE00000 )- ETH 4.288
0xb9de8c12caf8547d79b4c8e7cc6a77eff994af4c.CALL( )
- ETH 4.288
-
exit[RootChainManager (ln:1915)]
toExitPayload[RootChainManager (ln:1916)]getBranchMaskAsBytes[RootChainManager (ln:1917)]getBlockNumber[RootChainManager (ln:1922)]_getNibbleArray[RootChainManager (ln:1926)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:764)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:767)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:775)]
getReceiptLogIndex[RootChainManager (ln:1927)]getReceipt[RootChainManager (ln:1935)]getLog[RootChainManager (ln:1936)]getEmitter[RootChainManager (ln:1938)]getBranchMaskAsUint[RootChainManager (ln:1948)]verify[RootChainManager (ln:1955)]toRlpItem[MerklePatriciaProof (ln:666)]RLPItem[RLPReader (ln:267)]
toList[MerklePatriciaProof (ln:667)]isList[RLPReader (ln:306)]numItems[RLPReader (ln:307)]_payloadOffset[RLPReader (ln:432)]_itemLength[RLPReader (ln:435)]
_payloadOffset[RLPReader (ln:309)]_itemLength[RLPReader (ln:312)]RLPItem[RLPReader (ln:313)]
_getNibbleArray[MerklePatriciaProof (ln:672)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:764)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:767)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:775)]
toRlpBytes[MerklePatriciaProof (ln:680)]toList[MerklePatriciaProof (ln:684)]isList[RLPReader (ln:306)]numItems[RLPReader (ln:307)]_payloadOffset[RLPReader (ln:432)]_itemLength[RLPReader (ln:435)]
_payloadOffset[RLPReader (ln:309)]_itemLength[RLPReader (ln:312)]RLPItem[RLPReader (ln:313)]
toBytes[MerklePatriciaProof (ln:688)]payloadLocation[RLPReader (ln:416)]_payloadOffset[RLPReader (ln:290)]
copy[RLPReader (ln:422)]
toUintStrict[MerklePatriciaProof (ln:701)]_nibblesToTraverse[MerklePatriciaProof (ln:705)]_getNibbleArray[MerklePatriciaProof (ln:740)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:764)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:767)]_getNthNibbleOfBytes[MerklePatriciaProof (ln:775)]
toBytes[MerklePatriciaProof (ln:706)]payloadLocation[RLPReader (ln:416)]_payloadOffset[RLPReader (ln:290)]
copy[RLPReader (ln:422)]
toBytes[MerklePatriciaProof (ln:713)]payloadLocation[RLPReader (ln:416)]_payloadOffset[RLPReader (ln:290)]
copy[RLPReader (ln:422)]
toUintStrict[MerklePatriciaProof (ln:726)]
toBytes[RootChainManager (ln:1956)]getReceiptProof[RootChainManager (ln:1958)]getReceiptRoot[RootChainManager (ln:1959)]_checkBlockMembershipInCheckpoint[RootChainManager (ln:1964)]headerBlocks[RootChainManager (ln:1991)]checkMembership[RootChainManager (ln:1993)]sub[RootChainManager (ln:1997)]
getBlockNumber[RootChainManager (ln:1965)]getBlockTime[RootChainManager (ln:1966)]getTxRoot[RootChainManager (ln:1967)]getReceiptRoot[RootChainManager (ln:1968)]getHeaderNumber[RootChainManager (ln:1969)]getBlockProof[RootChainManager (ln:1970)]exitTokens[RootChainManager (ln:1972)]_msgSender[RootChainManager (ln:1973)]msgSender[RootChainManager (ln:1649)]
toRlpBytes[RootChainManager (ln:1975)]
File 1 of 5: RootChainManagerProxy
File 2 of 5: EtherPredicateProxy
File 3 of 5: RootChainManager
File 4 of 5: RootChainProxy
File 5 of 5: EtherPredicate
// File: contracts/common/Proxy/IERCProxy.sol
pragma solidity 0.6.6;
interface IERCProxy {
function proxyType() external pure returns (uint256 proxyTypeId);
function implementation() external view returns (address codeAddr);
}
// File: contracts/common/Proxy/Proxy.sol
pragma solidity 0.6.6;
abstract contract Proxy is IERCProxy {
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)
}
}
}
function proxyType() external virtual override pure returns (uint256 proxyTypeId) {
// Upgradeable proxy
proxyTypeId = 2;
}
function implementation() external virtual override view returns (address);
}
// File: contracts/common/Proxy/UpgradableProxy.sol
pragma solidity 0.6.6;
contract UpgradableProxy is Proxy {
event ProxyUpdated(address indexed _new, address indexed _old);
event ProxyOwnerUpdate(address _new, address _old);
bytes32 constant IMPLEMENTATION_SLOT = keccak256("matic.network.proxy.implementation");
bytes32 constant OWNER_SLOT = keccak256("matic.network.proxy.owner");
constructor(address _proxyTo) public {
setProxyOwner(msg.sender);
setImplementation(_proxyTo);
}
fallback() external payable {
delegatedFwd(loadImplementation(), msg.data);
}
receive() external payable {
delegatedFwd(loadImplementation(), msg.data);
}
modifier onlyProxyOwner() {
require(loadProxyOwner() == msg.sender, "NOT_OWNER");
_;
}
function proxyOwner() external view returns(address) {
return loadProxyOwner();
}
function loadProxyOwner() internal view returns(address) {
address _owner;
bytes32 position = OWNER_SLOT;
assembly {
_owner := sload(position)
}
return _owner;
}
function implementation() external override view returns (address) {
return loadImplementation();
}
function loadImplementation() internal view returns(address) {
address _impl;
bytes32 position = IMPLEMENTATION_SLOT;
assembly {
_impl := sload(position)
}
return _impl;
}
function transferProxyOwnership(address newOwner) public onlyProxyOwner {
require(newOwner != address(0), "ZERO_ADDRESS");
emit ProxyOwnerUpdate(newOwner, loadProxyOwner());
setProxyOwner(newOwner);
}
function setProxyOwner(address newOwner) private {
bytes32 position = OWNER_SLOT;
assembly {
sstore(position, newOwner)
}
}
function updateImplementation(address _newProxyTo) public onlyProxyOwner {
require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
emit ProxyUpdated(_newProxyTo, loadImplementation());
setImplementation(_newProxyTo);
}
function updateAndCall(address _newProxyTo, bytes memory data) payable public onlyProxyOwner {
updateImplementation(_newProxyTo);
(bool success, bytes memory returnData) = address(this).call{value: msg.value}(data);
require(success, string(returnData));
}
function setImplementation(address _newProxyTo) private {
bytes32 position = IMPLEMENTATION_SLOT;
assembly {
sstore(position, _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/RootChainManager/RootChainManagerProxy.sol
pragma solidity 0.6.6;
contract RootChainManagerProxy is UpgradableProxy {
constructor(address _proxyTo)
public
UpgradableProxy(_proxyTo)
{}
}File 2 of 5: EtherPredicateProxy
// File: contracts/common/Proxy/IERCProxy.sol
pragma solidity 0.6.6;
interface IERCProxy {
function proxyType() external pure returns (uint256 proxyTypeId);
function implementation() external view returns (address codeAddr);
}
// File: contracts/common/Proxy/Proxy.sol
pragma solidity 0.6.6;
abstract contract Proxy is IERCProxy {
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)
}
}
}
function proxyType() external virtual override pure returns (uint256 proxyTypeId) {
// Upgradeable proxy
proxyTypeId = 2;
}
function implementation() external virtual override view returns (address);
}
// File: contracts/common/Proxy/UpgradableProxy.sol
pragma solidity 0.6.6;
contract UpgradableProxy is Proxy {
event ProxyUpdated(address indexed _new, address indexed _old);
event ProxyOwnerUpdate(address _new, address _old);
bytes32 constant IMPLEMENTATION_SLOT = keccak256("matic.network.proxy.implementation");
bytes32 constant OWNER_SLOT = keccak256("matic.network.proxy.owner");
constructor(address _proxyTo) public {
setProxyOwner(msg.sender);
setImplementation(_proxyTo);
}
fallback() external payable {
delegatedFwd(loadImplementation(), msg.data);
}
receive() external payable {
delegatedFwd(loadImplementation(), msg.data);
}
modifier onlyProxyOwner() {
require(loadProxyOwner() == msg.sender, "NOT_OWNER");
_;
}
function proxyOwner() external view returns(address) {
return loadProxyOwner();
}
function loadProxyOwner() internal view returns(address) {
address _owner;
bytes32 position = OWNER_SLOT;
assembly {
_owner := sload(position)
}
return _owner;
}
function implementation() external override view returns (address) {
return loadImplementation();
}
function loadImplementation() internal view returns(address) {
address _impl;
bytes32 position = IMPLEMENTATION_SLOT;
assembly {
_impl := sload(position)
}
return _impl;
}
function transferProxyOwnership(address newOwner) public onlyProxyOwner {
require(newOwner != address(0), "ZERO_ADDRESS");
emit ProxyOwnerUpdate(newOwner, loadProxyOwner());
setProxyOwner(newOwner);
}
function setProxyOwner(address newOwner) private {
bytes32 position = OWNER_SLOT;
assembly {
sstore(position, newOwner)
}
}
function updateImplementation(address _newProxyTo) public onlyProxyOwner {
require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
emit ProxyUpdated(_newProxyTo, loadImplementation());
setImplementation(_newProxyTo);
}
function updateAndCall(address _newProxyTo, bytes memory data) payable public onlyProxyOwner {
updateImplementation(_newProxyTo);
(bool success, bytes memory returnData) = address(this).call{value: msg.value}(data);
require(success, string(returnData));
}
function setImplementation(address _newProxyTo) private {
bytes32 position = IMPLEMENTATION_SLOT;
assembly {
sstore(position, _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/TokenPredicates/EtherPredicateProxy.sol
pragma solidity 0.6.6;
contract EtherPredicateProxy is UpgradableProxy {
constructor(address _proxyTo)
public
UpgradableProxy(_proxyTo)
{}
}File 3 of 5: RootChainManager
// File: @openzeppelin/contracts/math/SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot 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-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File: contracts/root/RootChainManager/IRootChainManager.sol
pragma solidity 0.6.6;
interface IRootChainManager {
event TokenMapped(
address indexed rootToken,
address indexed childToken,
bytes32 indexed tokenType
);
event PredicateRegistered(
bytes32 indexed tokenType,
address indexed predicateAddress
);
function registerPredicate(bytes32 tokenType, address predicateAddress)
external;
function mapToken(
address rootToken,
address childToken,
bytes32 tokenType
) external;
function cleanMapToken(
address rootToken,
address childToken
) external;
function remapToken(
address rootToken,
address childToken,
bytes32 tokenType
) external;
function depositEtherFor(address user) external payable;
function depositFor(
address user,
address rootToken,
bytes calldata depositData
) external;
function exit(bytes calldata inputData) external;
}
// File: contracts/root/StateSender/IStateSender.sol
pragma solidity 0.6.6;
interface IStateSender {
function syncState(address receiver, bytes calldata data) external;
}
// File: contracts/root/ICheckpointManager.sol
pragma solidity 0.6.6;
contract ICheckpointManager {
struct HeaderBlock {
bytes32 root;
uint256 start;
uint256 end;
uint256 createdAt;
address proposer;
}
/**
* @notice mapping of checkpoint header numbers to block details
* @dev These checkpoints are submited by plasma contracts
*/
mapping(uint256 => HeaderBlock) public headerBlocks;
}
// File: contracts/root/RootChainManager/RootChainManagerStorage.sol
pragma solidity 0.6.6;
abstract contract RootChainManagerStorage {
mapping(bytes32 => address) public typeToPredicate;
mapping(address => address) public rootToChildToken;
mapping(address => address) public childToRootToken;
mapping(address => bytes32) public tokenToType;
mapping(bytes32 => bool) public processedExits;
IStateSender internal _stateSender;
ICheckpointManager internal _checkpointManager;
address public childChainManagerAddress;
}
// File: contracts/lib/RLPReader.sol
/*
* @author Hamdi Allam hamdi.allam97@gmail.com
* Please reach out with any questions or concerns
* https://github.com/hamdiallam/Solidity-RLP/blob/e681e25a376dbd5426b509380bc03446f05d0f97/contracts/RLPReader.sol
*/
pragma solidity 0.6.6;
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 the RLP item.
*/
function rlpLen(RLPItem memory item) internal pure returns (uint) {
return item.len;
}
/*
* @param the RLP item.
* @return (memPtr, len) pair: location of the item's payload in memory.
*/
function payloadLocation(RLPItem memory item) internal pure returns (uint, uint) {
uint offset = _payloadOffset(item.memPtr);
uint memPtr = item.memPtr + offset;
uint len = item.len - offset; // data length
return (memPtr, len);
}
/*
* @param the RLP item.
*/
function payloadLen(RLPItem memory item) internal pure returns (uint) {
(, uint len) = payloadLocation(item);
return len;
}
/*
* @param the RLP item containing the encoded list.
*/
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;
}
/*
* @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
* @return keccak256 hash of RLP encoded bytes.
*/
function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
uint256 ptr = item.memPtr;
uint256 len = item.len;
bytes32 result;
assembly {
result := keccak256(ptr, len)
}
return result;
}
/*
* @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
* @return keccak256 hash of the item payload.
*/
function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
(uint memPtr, uint len) = payloadLocation(item);
bytes32 result;
assembly {
result := keccak256(memPtr, len)
}
return result;
}
/** 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 memPtr, uint len) = payloadLocation(item);
uint result;
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 memPtr, uint len) = payloadLocation(item);
bytes memory result = new bytes(len);
uint destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(memPtr, 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;
}
if (len > 0) {
// 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: contracts/lib/ExitPayloadReader.sol
pragma solidity 0.6.6;
library ExitPayloadReader {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant WORD_SIZE = 32;
struct ExitPayload {
RLPReader.RLPItem[] data;
}
struct Receipt {
RLPReader.RLPItem[] data;
bytes raw;
uint256 logIndex;
}
struct Log {
RLPReader.RLPItem data;
RLPReader.RLPItem[] list;
}
struct LogTopics {
RLPReader.RLPItem[] data;
}
// copy paste of private copy() from RLPReader to avoid changing of existing contracts
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;
}
if (len == 0) return;
// 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))
}
}
function toExitPayload(bytes memory data)
internal
pure
returns (ExitPayload memory)
{
RLPReader.RLPItem[] memory payloadData = data
.toRlpItem()
.toList();
return ExitPayload(payloadData);
}
function getHeaderNumber(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[0].toUint();
}
function getBlockProof(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[1].toBytes();
}
function getBlockNumber(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[2].toUint();
}
function getBlockTime(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[3].toUint();
}
function getTxRoot(ExitPayload memory payload) internal pure returns(bytes32) {
return bytes32(payload.data[4].toUint());
}
function getReceiptRoot(ExitPayload memory payload) internal pure returns(bytes32) {
return bytes32(payload.data[5].toUint());
}
function getReceipt(ExitPayload memory payload) internal pure returns(Receipt memory receipt) {
receipt.raw = payload.data[6].toBytes();
RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();
if (receiptItem.isList()) {
// legacy tx
receipt.data = receiptItem.toList();
} else {
// pop first byte before parsting receipt
bytes memory typedBytes = receipt.raw;
bytes memory result = new bytes(typedBytes.length - 1);
uint256 srcPtr;
uint256 destPtr;
assembly {
srcPtr := add(33, typedBytes)
destPtr := add(0x20, result)
}
copy(srcPtr, destPtr, result.length);
receipt.data = result.toRlpItem().toList();
}
receipt.logIndex = getReceiptLogIndex(payload);
return receipt;
}
function getReceiptProof(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[7].toBytes();
}
function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns(bytes memory) {
return payload.data[8].toBytes();
}
function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[8].toUint();
}
function getReceiptLogIndex(ExitPayload memory payload) internal pure returns(uint256) {
return payload.data[9].toUint();
}
// Receipt methods
function toBytes(Receipt memory receipt) internal pure returns(bytes memory) {
return receipt.raw;
}
function getLog(Receipt memory receipt) internal pure returns(Log memory) {
RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
return Log(logData, logData.toList());
}
// Log methods
function getEmitter(Log memory log) internal pure returns(address) {
return RLPReader.toAddress(log.list[0]);
}
function getTopics(Log memory log) internal pure returns(LogTopics memory) {
return LogTopics(log.list[1].toList());
}
function getData(Log memory log) internal pure returns(bytes memory) {
return log.list[2].toBytes();
}
function toRlpBytes(Log memory log) internal pure returns(bytes memory) {
return log.data.toRlpBytes();
}
// LogTopics methods
function getField(LogTopics memory topics, uint256 index) internal pure returns(RLPReader.RLPItem memory) {
return topics.data[index];
}
}
// File: contracts/lib/MerklePatriciaProof.sol
/*
* @title MerklePatriciaVerifier
* @author Sam Mayo (sammayo888@gmail.com)
*
* @dev Library for verifing merkle patricia proofs.
*/
pragma solidity 0.6.6;
library MerklePatriciaProof {
/*
* @dev Verifies a merkle patricia proof.
* @param value The terminating value in the trie.
* @param encodedPath The path in the trie leading to value.
* @param rlpParentNodes The rlp encoded stack of nodes.
* @param root The root hash of the trie.
* @return The boolean validity of the proof.
*/
function verify(
bytes memory value,
bytes memory encodedPath,
bytes memory rlpParentNodes,
bytes32 root
) internal pure returns (bool) {
RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
bytes memory currentNode;
RLPReader.RLPItem[] memory currentNodeList;
bytes32 nodeKey = root;
uint256 pathPtr = 0;
bytes memory path = _getNibbleArray(encodedPath);
if (path.length == 0) {
return false;
}
for (uint256 i = 0; i < parentNodes.length; i++) {
if (pathPtr > path.length) {
return false;
}
currentNode = RLPReader.toRlpBytes(parentNodes[i]);
if (nodeKey != keccak256(currentNode)) {
return false;
}
currentNodeList = RLPReader.toList(parentNodes[i]);
if (currentNodeList.length == 17) {
if (pathPtr == path.length) {
if (
keccak256(RLPReader.toBytes(currentNodeList[16])) ==
keccak256(value)
) {
return true;
} else {
return false;
}
}
uint8 nextPathNibble = uint8(path[pathPtr]);
if (nextPathNibble > 16) {
return false;
}
nodeKey = bytes32(
RLPReader.toUintStrict(currentNodeList[nextPathNibble])
);
pathPtr += 1;
} else if (currentNodeList.length == 2) {
uint256 traversed = _nibblesToTraverse(
RLPReader.toBytes(currentNodeList[0]),
path,
pathPtr
);
if (pathPtr + traversed == path.length) {
//leaf node
if (
keccak256(RLPReader.toBytes(currentNodeList[1])) ==
keccak256(value)
) {
return true;
} else {
return false;
}
}
//extension node
if (traversed == 0) {
return false;
}
pathPtr += traversed;
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
} else {
return false;
}
}
}
function _nibblesToTraverse(
bytes memory encodedPartialPath,
bytes memory path,
uint256 pathPtr
) private pure returns (uint256) {
uint256 len = 0;
// encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
// and slicedPath have elements that are each one hex character (1 nibble)
bytes memory partialPath = _getNibbleArray(encodedPartialPath);
bytes memory slicedPath = new bytes(partialPath.length);
// pathPtr counts nibbles in path
// partialPath.length is a number of nibbles
for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
bytes1 pathNibble = path[i];
slicedPath[i - pathPtr] = pathNibble;
}
if (keccak256(partialPath) == keccak256(slicedPath)) {
len = partialPath.length;
} else {
len = 0;
}
return len;
}
// bytes b must be hp encoded
function _getNibbleArray(bytes memory b)
internal
pure
returns (bytes memory)
{
bytes memory nibbles = "";
if (b.length > 0) {
uint8 offset;
uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
if (hpNibble == 1 || hpNibble == 3) {
nibbles = new bytes(b.length * 2 - 1);
bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
nibbles[0] = oddNibble;
offset = 1;
} else {
nibbles = new bytes(b.length * 2 - 2);
offset = 0;
}
for (uint256 i = offset; i < nibbles.length; i++) {
nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
}
}
return nibbles;
}
function _getNthNibbleOfBytes(uint256 n, bytes memory str)
private
pure
returns (bytes1)
{
return
bytes1(
n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10
);
}
}
// File: contracts/lib/Merkle.sol
pragma solidity 0.6.6;
library Merkle {
function checkMembership(
bytes32 leaf,
uint256 index,
bytes32 rootHash,
bytes memory proof
) internal pure returns (bool) {
require(proof.length % 32 == 0, "Invalid proof length");
uint256 proofHeight = proof.length / 32;
// Proof of size n means, height of the tree is n+1.
// In a tree of height n+1, max #leafs possible is 2 ^ n
require(index < 2 ** proofHeight, "Leaf index is too big");
bytes32 proofElement;
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
assembly {
proofElement := mload(add(proof, i))
}
if (index % 2 == 0) {
computedHash = keccak256(
abi.encodePacked(computedHash, proofElement)
);
} else {
computedHash = keccak256(
abi.encodePacked(proofElement, computedHash)
);
}
index = index / 2;
}
return computedHash == rootHash;
}
}
// File: contracts/root/TokenPredicates/ITokenPredicate.sol
pragma solidity 0.6.6;
/// @title Token predicate interface for all pos portal predicates
/// @notice Abstract interface that defines methods for custom predicates
interface ITokenPredicate {
/**
* @notice Deposit tokens into pos portal
* @dev When `depositor` deposits tokens into pos portal, tokens get locked into predicate contract.
* @param depositor Address who wants to deposit tokens
* @param depositReceiver Address (address) who wants to receive tokens on side chain
* @param rootToken Token which gets deposited
* @param depositData Extra data for deposit (amount for ERC20, token id for ERC721 etc.) [ABI encoded]
*/
function lockTokens(
address depositor,
address depositReceiver,
address rootToken,
bytes calldata depositData
) external;
/**
* @notice Validates and processes exit while withdraw process
* @dev Validates exit log emitted on sidechain. Reverts if validation fails.
* @dev Processes withdraw based on custom logic. Example: transfer ERC20/ERC721, mint ERC721 if mintable withdraw
* @param sender Address
* @param rootToken Token which gets withdrawn
* @param logRLPList Valid sidechain log for data like amount, token id etc.
*/
function exitTokens(
address sender,
address rootToken,
bytes calldata logRLPList
) external;
}
// File: contracts/common/Initializable.sol
pragma solidity 0.6.6;
contract Initializable {
bool inited = false;
modifier initializer() {
require(!inited, "already inited");
_;
inited = true;
}
}
// File: contracts/common/EIP712Base.sol
pragma solidity 0.6.6;
contract EIP712Base is Initializable {
struct EIP712Domain {
string name;
string version;
address verifyingContract;
bytes32 salt;
}
string constant public ERC712_VERSION = "1";
bytes32 internal constant EIP712_DOMAIN_TYPEHASH = keccak256(
bytes(
"EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)"
)
);
bytes32 internal domainSeperator;
// supposed to be called once while initializing.
// one of the contractsa that inherits this contract follows proxy pattern
// so it is not possible to do this in a constructor
function _initializeEIP712(
string memory name
)
internal
initializer
{
_setDomainSeperator(name);
}
function _setDomainSeperator(string memory name) internal {
domainSeperator = keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(name)),
keccak256(bytes(ERC712_VERSION)),
address(this),
bytes32(getChainId())
)
);
}
function getDomainSeperator() public view returns (bytes32) {
return domainSeperator;
}
function getChainId() public pure returns (uint256) {
uint256 id;
assembly {
id := chainid()
}
return id;
}
/**
* Accept message hash and returns hash message in EIP712 compatible form
* So that it can be used to recover signer from signature signed using EIP712 formatted data
* https://eips.ethereum.org/EIPS/eip-712
* "\\\\x19" makes the encoding deterministic
* "\\\\x01" is the version byte to make it compatible to EIP-191
*/
function toTypedMessageHash(bytes32 messageHash)
internal
view
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\\x19\\x01", getDomainSeperator(), messageHash)
);
}
}
// File: contracts/common/NativeMetaTransaction.sol
pragma solidity 0.6.6;
contract NativeMetaTransaction is EIP712Base {
using SafeMath for uint256;
bytes32 private constant META_TRANSACTION_TYPEHASH = keccak256(
bytes(
"MetaTransaction(uint256 nonce,address from,bytes functionSignature)"
)
);
event MetaTransactionExecuted(
address userAddress,
address payable relayerAddress,
bytes functionSignature
);
mapping(address => uint256) nonces;
/*
* Meta transaction structure.
* No point of including value field here as if user is doing value transfer then he has the funds to pay for gas
* He should call the desired function directly in that case.
*/
struct MetaTransaction {
uint256 nonce;
address from;
bytes functionSignature;
}
function executeMetaTransaction(
address userAddress,
bytes memory functionSignature,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) public payable returns (bytes memory) {
MetaTransaction memory metaTx = MetaTransaction({
nonce: nonces[userAddress],
from: userAddress,
functionSignature: functionSignature
});
require(
verify(userAddress, metaTx, sigR, sigS, sigV),
"Signer and signature do not match"
);
// increase nonce for user (to avoid re-use)
nonces[userAddress] = nonces[userAddress].add(1);
emit MetaTransactionExecuted(
userAddress,
msg.sender,
functionSignature
);
// Append userAddress and relayer address at the end to extract it from calling context
(bool success, bytes memory returnData) = address(this).call(
abi.encodePacked(functionSignature, userAddress)
);
require(success, "Function call not successful");
return returnData;
}
function hashMetaTransaction(MetaTransaction memory metaTx)
internal
pure
returns (bytes32)
{
return
keccak256(
abi.encode(
META_TRANSACTION_TYPEHASH,
metaTx.nonce,
metaTx.from,
keccak256(metaTx.functionSignature)
)
);
}
function getNonce(address user) public view returns (uint256 nonce) {
nonce = nonces[user];
}
function verify(
address signer,
MetaTransaction memory metaTx,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) internal view returns (bool) {
require(signer != address(0), "NativeMetaTransaction: INVALID_SIGNER");
return
signer ==
ecrecover(
toTypedMessageHash(hashMetaTransaction(metaTx)),
sigV,
sigR,
sigS
);
}
}
// File: @openzeppelin/contracts/utils/EnumerableSet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint256(_at(set._inner, index)));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
// File: @openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// File: @openzeppelin/contracts/GSN/Context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: @openzeppelin/contracts/access/AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context {
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
}
// File: contracts/common/AccessControlMixin.sol
pragma solidity 0.6.6;
contract AccessControlMixin is AccessControl {
string private _revertMsg;
function _setupContractId(string memory contractId) internal {
_revertMsg = string(abi.encodePacked(contractId, ": INSUFFICIENT_PERMISSIONS"));
}
modifier only(bytes32 role) {
require(
hasRole(role, _msgSender()),
_revertMsg
);
_;
}
}
// File: contracts/common/ContextMixin.sol
pragma solidity 0.6.6;
abstract contract ContextMixin {
function msgSender()
internal
view
returns (address payable sender)
{
if (msg.sender == address(this)) {
bytes memory array = msg.data;
uint256 index = msg.data.length;
assembly {
// Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
sender := and(
mload(add(array, index)),
0xffffffffffffffffffffffffffffffffffffffff
)
}
} else {
sender = msg.sender;
}
return sender;
}
}
// File: contracts/root/RootChainManager/RootChainManager.sol
pragma solidity 0.6.6;
contract RootChainManager is
IRootChainManager,
Initializable,
AccessControl, // included to match old storage layout while upgrading
RootChainManagerStorage, // created to match old storage layout while upgrading
AccessControlMixin,
NativeMetaTransaction,
ContextMixin
{
using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.Receipt;
using Merkle for bytes32;
using SafeMath for uint256;
// maybe DEPOSIT and MAP_TOKEN can be reduced to bytes4
bytes32 public constant DEPOSIT = keccak256("DEPOSIT");
bytes32 public constant MAP_TOKEN = keccak256("MAP_TOKEN");
address public constant ETHER_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
bytes32 public constant MAPPER_ROLE = keccak256("MAPPER_ROLE");
function _msgSender()
internal
override
view
returns (address payable sender)
{
return ContextMixin.msgSender();
}
/**
* @notice Deposit ether by directly sending to the contract
* The account sending ether receives WETH on child chain
*/
receive() external payable {
_depositEtherFor(_msgSender());
}
/**
* @notice Initialize the contract after it has been proxified
* @dev meant to be called once immediately after deployment
* @param _owner the account that should be granted admin role
*/
function initialize(
address _owner
)
external
initializer
{
_initializeEIP712("RootChainManager");
_setupContractId("RootChainManager");
_setupRole(DEFAULT_ADMIN_ROLE, _owner);
_setupRole(MAPPER_ROLE, _owner);
}
// adding seperate function setupContractId since initialize is already called with old implementation
function setupContractId()
external
only(DEFAULT_ADMIN_ROLE)
{
_setupContractId("RootChainManager");
}
// adding seperate function initializeEIP712 since initialize is already called with old implementation
function initializeEIP712()
external
only(DEFAULT_ADMIN_ROLE)
{
_setDomainSeperator("RootChainManager");
}
/**
* @notice Set the state sender, callable only by admins
* @dev This should be the state sender from plasma contracts
* It is used to send bytes from root to child chain
* @param newStateSender address of state sender contract
*/
function setStateSender(address newStateSender)
external
only(DEFAULT_ADMIN_ROLE)
{
require(newStateSender != address(0), "RootChainManager: BAD_NEW_STATE_SENDER");
_stateSender = IStateSender(newStateSender);
}
/**
* @notice Get the address of contract set as state sender
* @return The address of state sender contract
*/
function stateSenderAddress() external view returns (address) {
return address(_stateSender);
}
/**
* @notice Set the checkpoint manager, callable only by admins
* @dev This should be the plasma contract responsible for keeping track of checkpoints
* @param newCheckpointManager address of checkpoint manager contract
*/
function setCheckpointManager(address newCheckpointManager)
external
only(DEFAULT_ADMIN_ROLE)
{
require(newCheckpointManager != address(0), "RootChainManager: BAD_NEW_CHECKPOINT_MANAGER");
_checkpointManager = ICheckpointManager(newCheckpointManager);
}
/**
* @notice Get the address of contract set as checkpoint manager
* @return The address of checkpoint manager contract
*/
function checkpointManagerAddress() external view returns (address) {
return address(_checkpointManager);
}
/**
* @notice Set the child chain manager, callable only by admins
* @dev This should be the contract responsible to receive deposit bytes on child chain
* @param newChildChainManager address of child chain manager contract
*/
function setChildChainManagerAddress(address newChildChainManager)
external
only(DEFAULT_ADMIN_ROLE)
{
require(newChildChainManager != address(0x0), "RootChainManager: INVALID_CHILD_CHAIN_ADDRESS");
childChainManagerAddress = newChildChainManager;
}
/**
* @notice Register a token predicate address against its type, callable only by ADMIN
* @dev A predicate is a contract responsible to process the token specific logic while locking or exiting tokens
* @param tokenType bytes32 unique identifier for the token type
* @param predicateAddress address of token predicate address
*/
function registerPredicate(bytes32 tokenType, address predicateAddress)
external
override
only(DEFAULT_ADMIN_ROLE)
{
typeToPredicate[tokenType] = predicateAddress;
emit PredicateRegistered(tokenType, predicateAddress);
}
/**
* @notice Map a token to enable its movement via the PoS Portal, callable only by mappers
* @param rootToken address of token on root chain
* @param childToken address of token on child chain
* @param tokenType bytes32 unique identifier for the token type
*/
function mapToken(
address rootToken,
address childToken,
bytes32 tokenType
) external override only(MAPPER_ROLE) {
// explicit check if token is already mapped to avoid accidental remaps
require(
rootToChildToken[rootToken] == address(0) &&
childToRootToken[childToken] == address(0),
"RootChainManager: ALREADY_MAPPED"
);
_mapToken(rootToken, childToken, tokenType);
}
/**
* @notice Clean polluted token mapping
* @param rootToken address of token on root chain. Since rename token was introduced later stage,
* clean method is used to clean pollulated mapping
*/
function cleanMapToken(
address rootToken,
address childToken
) external override only(DEFAULT_ADMIN_ROLE) {
rootToChildToken[rootToken] = address(0);
childToRootToken[childToken] = address(0);
tokenToType[rootToken] = bytes32(0);
emit TokenMapped(rootToken, childToken, tokenToType[rootToken]);
}
/**
* @notice Remap a token that has already been mapped, properly cleans up old mapping
* Callable only by ADMIN
* @param rootToken address of token on root chain
* @param childToken address of token on child chain
* @param tokenType bytes32 unique identifier for the token type
*/
function remapToken(
address rootToken,
address childToken,
bytes32 tokenType
) external override only(DEFAULT_ADMIN_ROLE) {
// cleanup old mapping
address oldChildToken = rootToChildToken[rootToken];
address oldRootToken = childToRootToken[childToken];
if (rootToChildToken[oldRootToken] != address(0)) {
rootToChildToken[oldRootToken] = address(0);
tokenToType[oldRootToken] = bytes32(0);
}
if (childToRootToken[oldChildToken] != address(0)) {
childToRootToken[oldChildToken] = address(0);
}
_mapToken(rootToken, childToken, tokenType);
}
function _mapToken(
address rootToken,
address childToken,
bytes32 tokenType
) private {
require(
typeToPredicate[tokenType] != address(0x0),
"RootChainManager: TOKEN_TYPE_NOT_SUPPORTED"
);
rootToChildToken[rootToken] = childToken;
childToRootToken[childToken] = rootToken;
tokenToType[rootToken] = tokenType;
emit TokenMapped(rootToken, childToken, tokenType);
bytes memory syncData = abi.encode(rootToken, childToken, tokenType);
_stateSender.syncState(
childChainManagerAddress,
abi.encode(MAP_TOKEN, syncData)
);
}
/**
* @notice Move ether from root to child chain, accepts ether transfer
* Keep in mind this ether cannot be used to pay gas on child chain
* Use Matic tokens deposited using plasma mechanism for that
* @param user address of account that should receive WETH on child chain
*/
function depositEtherFor(address user) external override payable {
_depositEtherFor(user);
}
/**
* @notice Move tokens from root to child chain
* @dev This mechanism supports arbitrary tokens as long as its predicate has been registered and the token is mapped
* @param user address of account that should receive this deposit on child chain
* @param rootToken address of token that is being deposited
* @param depositData bytes data that is sent to predicate and child token contracts to handle deposit
*/
function depositFor(
address user,
address rootToken,
bytes calldata depositData
) external override {
require(
rootToken != ETHER_ADDRESS,
"RootChainManager: INVALID_ROOT_TOKEN"
);
_depositFor(user, rootToken, depositData);
}
function _depositEtherFor(address user) private {
bytes memory depositData = abi.encode(msg.value);
_depositFor(user, ETHER_ADDRESS, depositData);
// payable(typeToPredicate[tokenToType[ETHER_ADDRESS]]).transfer(msg.value);
// transfer doesn't work as expected when receiving contract is proxified so using call
(bool success, /* bytes memory data */) = typeToPredicate[tokenToType[ETHER_ADDRESS]].call{value: msg.value}("");
if (!success) {
revert("RootChainManager: ETHER_TRANSFER_FAILED");
}
}
function _depositFor(
address user,
address rootToken,
bytes memory depositData
) private {
bytes32 tokenType = tokenToType[rootToken];
require(
rootToChildToken[rootToken] != address(0x0) &&
tokenType != 0,
"RootChainManager: TOKEN_NOT_MAPPED"
);
address predicateAddress = typeToPredicate[tokenType];
require(
predicateAddress != address(0),
"RootChainManager: INVALID_TOKEN_TYPE"
);
require(
user != address(0),
"RootChainManager: INVALID_USER"
);
ITokenPredicate(predicateAddress).lockTokens(
_msgSender(),
user,
rootToken,
depositData
);
bytes memory syncData = abi.encode(user, rootToken, depositData);
_stateSender.syncState(
childChainManagerAddress,
abi.encode(DEPOSIT, syncData)
);
}
/**
* @notice exit tokens by providing proof
* @dev This function verifies if the transaction actually happened on child chain
* the transaction log is then sent to token predicate to handle it accordingly
*
* @param inputData RLP encoded data of the reference tx containing following list of fields
* 0 - headerNumber - Checkpoint header block number containing the reference tx
* 1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
* 2 - blockNumber - Block number containing the reference tx on child chain
* 3 - blockTime - Reference tx block time
* 4 - txRoot - Transactions root of block
* 5 - receiptRoot - Receipts root of block
* 6 - receipt - Receipt of the reference transaction
* 7 - receiptProof - Merkle proof of the reference receipt
* 8 - branchMask - 32 bits denoting the path of receipt in merkle tree
* 9 - receiptLogIndex - Log Index to read from the receipt
*/
function exit(bytes calldata inputData) external override {
ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();
bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
// checking if exit has already been processed
// unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
bytes32 exitHash = keccak256(
abi.encodePacked(
payload.getBlockNumber(),
// first 2 nibbles are dropped while generating nibble array
// this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
// so converting to nibble array and then hashing it
MerklePatriciaProof._getNibbleArray(branchMaskBytes),
payload.getReceiptLogIndex()
)
);
require(
processedExits[exitHash] == false,
"RootChainManager: EXIT_ALREADY_PROCESSED"
);
processedExits[exitHash] = true;
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
ExitPayloadReader.Log memory log = receipt.getLog();
// log should be emmited only by the child token
address rootToken = childToRootToken[log.getEmitter()];
require(
rootToken != address(0),
"RootChainManager: TOKEN_NOT_MAPPED"
);
address predicateAddress = typeToPredicate[
tokenToType[rootToken]
];
// branch mask can be maximum 32 bits
require(
payload.getBranchMaskAsUint() &
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000 ==
0,
"RootChainManager: INVALID_BRANCH_MASK"
);
// verify receipt inclusion
require(
MerklePatriciaProof.verify(
receipt.toBytes(),
branchMaskBytes,
payload.getReceiptProof(),
payload.getReceiptRoot()
),
"RootChainManager: INVALID_PROOF"
);
// verify checkpoint inclusion
_checkBlockMembershipInCheckpoint(
payload.getBlockNumber(),
payload.getBlockTime(),
payload.getTxRoot(),
payload.getReceiptRoot(),
payload.getHeaderNumber(),
payload.getBlockProof()
);
ITokenPredicate(predicateAddress).exitTokens(
_msgSender(),
rootToken,
log.toRlpBytes()
);
}
function _checkBlockMembershipInCheckpoint(
uint256 blockNumber,
uint256 blockTime,
bytes32 txRoot,
bytes32 receiptRoot,
uint256 headerNumber,
bytes memory blockProof
) private view returns (uint256) {
(
bytes32 headerRoot,
uint256 startBlock,
,
uint256 createdAt,
) = _checkpointManager.headerBlocks(headerNumber);
require(
keccak256(
abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)
)
.checkMembership(
blockNumber.sub(startBlock),
headerRoot,
blockProof
),
"RootChainManager: INVALID_HEADER"
);
return createdAt;
}
}
File 4 of 5: 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 5 of 5: EtherPredicate
// File: @openzeppelin/contracts/utils/EnumerableSet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint256(_at(set._inner, index)));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
// File: @openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// File: @openzeppelin/contracts/GSN/Context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File: @openzeppelin/contracts/access/AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context {
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
}
// File: contracts/common/AccessControlMixin.sol
pragma solidity 0.6.6;
contract AccessControlMixin is AccessControl {
string private _revertMsg;
function _setupContractId(string memory contractId) internal {
_revertMsg = string(abi.encodePacked(contractId, ": INSUFFICIENT_PERMISSIONS"));
}
modifier only(bytes32 role) {
require(
hasRole(role, _msgSender()),
_revertMsg
);
_;
}
}
// File: contracts/lib/RLPReader.sol
/*
* @author Hamdi Allam hamdi.allam97@gmail.com
* Please reach out with any questions or concerns
* https://github.com/hamdiallam/Solidity-RLP/blob/e681e25a376dbd5426b509380bc03446f05d0f97/contracts/RLPReader.sol
*/
pragma solidity 0.6.6;
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 the RLP item.
*/
function rlpLen(RLPItem memory item) internal pure returns (uint) {
return item.len;
}
/*
* @param the RLP item.
* @return (memPtr, len) pair: location of the item's payload in memory.
*/
function payloadLocation(RLPItem memory item) internal pure returns (uint, uint) {
uint offset = _payloadOffset(item.memPtr);
uint memPtr = item.memPtr + offset;
uint len = item.len - offset; // data length
return (memPtr, len);
}
/*
* @param the RLP item.
*/
function payloadLen(RLPItem memory item) internal pure returns (uint) {
(, uint len) = payloadLocation(item);
return len;
}
/*
* @param the RLP item containing the encoded list.
*/
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;
}
/*
* @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
* @return keccak256 hash of RLP encoded bytes.
*/
function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
uint256 ptr = item.memPtr;
uint256 len = item.len;
bytes32 result;
assembly {
result := keccak256(ptr, len)
}
return result;
}
/*
* @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
* @return keccak256 hash of the item payload.
*/
function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
(uint memPtr, uint len) = payloadLocation(item);
bytes32 result;
assembly {
result := keccak256(memPtr, len)
}
return result;
}
/** 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 memPtr, uint len) = payloadLocation(item);
uint result;
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 memPtr, uint len) = payloadLocation(item);
bytes memory result = new bytes(len);
uint destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(memPtr, 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;
}
if (len > 0) {
// 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: contracts/root/TokenPredicates/ITokenPredicate.sol
pragma solidity 0.6.6;
/// @title Token predicate interface for all pos portal predicates
/// @notice Abstract interface that defines methods for custom predicates
interface ITokenPredicate {
/**
* @notice Deposit tokens into pos portal
* @dev When `depositor` deposits tokens into pos portal, tokens get locked into predicate contract.
* @param depositor Address who wants to deposit tokens
* @param depositReceiver Address (address) who wants to receive tokens on side chain
* @param rootToken Token which gets deposited
* @param depositData Extra data for deposit (amount for ERC20, token id for ERC721 etc.) [ABI encoded]
*/
function lockTokens(
address depositor,
address depositReceiver,
address rootToken,
bytes calldata depositData
) external;
/**
* @notice Validates and processes exit while withdraw process
* @dev Validates exit log emitted on sidechain. Reverts if validation fails.
* @dev Processes withdraw based on custom logic. Example: transfer ERC20/ERC721, mint ERC721 if mintable withdraw
* @param sender Address
* @param rootToken Token which gets withdrawn
* @param logRLPList Valid sidechain log for data like amount, token id etc.
*/
function exitTokens(
address sender,
address rootToken,
bytes calldata logRLPList
) external;
}
// File: contracts/common/Initializable.sol
pragma solidity 0.6.6;
contract Initializable {
bool inited = false;
modifier initializer() {
require(!inited, "already inited");
_;
inited = true;
}
}
// File: contracts/root/TokenPredicates/EtherPredicate.sol
pragma solidity 0.6.6;
contract EtherPredicate is ITokenPredicate, AccessControlMixin, Initializable {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
bytes32 public constant TOKEN_TYPE = keccak256("Ether");
bytes32 public constant TRANSFER_EVENT_SIG = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
event LockedEther(
address indexed depositor,
address indexed depositReceiver,
uint256 amount
);
event ExitedEther(
address indexed exitor,
uint256 amount
);
constructor() public {}
function initialize(address _owner) external initializer {
_setupContractId("EtherPredicate");
_setupRole(DEFAULT_ADMIN_ROLE, _owner);
_setupRole(MANAGER_ROLE, _owner);
}
/**
* @notice Receive Ether to lock for deposit, callable only by manager
*/
receive() external payable only(MANAGER_ROLE) {}
/**
* @notice handle ether lock, callable only by manager
* @param depositor Address who wants to deposit tokens
* @param depositReceiver Address (address) who wants to receive tokens on child chain
* @param depositData ABI encoded amount
*/
function lockTokens(
address depositor,
address depositReceiver,
address,
bytes calldata depositData
)
external
override
only(MANAGER_ROLE)
{
uint256 amount = abi.decode(depositData, (uint256));
emit LockedEther(depositor, depositReceiver, amount);
}
/**
* @notice Validates log signature, from and to address
* then sends the correct amount to withdrawer
* callable only by manager
* @param log Valid ERC20 burn log from child chain
*/
function exitTokens(
address,
address,
bytes memory log
)
public
override
only(MANAGER_ROLE)
{
RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList();
RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics
require(
bytes32(logTopicRLPList[0].toUint()) == TRANSFER_EVENT_SIG, // topic0 is event sig
"EtherPredicate: INVALID_SIGNATURE"
);
address withdrawer = address(logTopicRLPList[1].toUint()); // topic1 is from address
require(
address(logTopicRLPList[2].toUint()) == address(0), // topic2 is to address
"EtherPredicate: INVALID_RECEIVER"
);
emit ExitedEther(withdrawer, logRLPList[2].toUint());
(bool success, /* bytes memory data */) = withdrawer.call{value: logRLPList[2].toUint()}("");
if (!success) {
revert("EtherPredicate: ETHER_TRANSFER_FAILED");
}
}
}