// Sources flattened with hardhat v2.4.3 https://hardhat.org
// File @animoca/ethereum-contracts-core-1.1.1/contracts/utils/types/AddressIsContract.sol@v1.1.1
// SPDX-License-Identifier: MIT
// Partially derived from OpenZeppelin:
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/406c83649bd6169fc1b578e08506d78f0873b276/contracts/utils/Address.sol
pragma solidity >=0.7.6 <0.8.0;
/**
* @dev Upgrades the address type to check if it is a contract.
*/
library AddressIsContract {
/**
* @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) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
}
// File @animoca/ethereum-contracts-core-1.1.1/contracts/utils/ERC20Wrapper.sol@v1.1.1
pragma solidity >=0.7.6 <0.8.0;
/**
* @title ERC20Wrapper
* Wraps ERC20 functions to support non-standard implementations which do not return a bool value.
* Calls to the wrapped functions revert only if they throw or if they return false.
*/
library ERC20Wrapper {
using AddressIsContract for address;
function wrappedTransfer(
IWrappedERC20 token,
address to,
uint256 value
) internal {
_callWithOptionalReturnData(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function wrappedTransferFrom(
IWrappedERC20 token,
address from,
address to,
uint256 value
) internal {
_callWithOptionalReturnData(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function wrappedApprove(
IWrappedERC20 token,
address spender,
uint256 value
) internal {
_callWithOptionalReturnData(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function _callWithOptionalReturnData(IWrappedERC20 token, bytes memory callData) internal {
address target = address(token);
require(target.isContract(), "ERC20Wrapper: non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory data) = target.call(callData);
if (success) {
if (data.length != 0) {
require(abi.decode(data, (bool)), "ERC20Wrapper: operation failed");
}
} else {
// revert using a standard revert message
if (data.length == 0) {
revert("ERC20Wrapper: operation failed");
}
// revert using the revert message coming from the call
assembly {
let size := mload(data)
revert(add(32, data), size)
}
}
}
}
interface IWrappedERC20 {
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
}
// File @animoca/ethereum-contracts-core-1.1.1/contracts/metatx/ManagedIdentity.sol@v1.1.1
pragma solidity >=0.7.6 <0.8.0;
/*
* 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.
*/
abstract contract ManagedIdentity {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
return msg.data;
}
}
// File @animoca/ethereum-contracts-core-1.1.1/contracts/bridging/ITokenPredicate.sol@v1.1.1
pragma solidity >=0.7.6 <0.8.0;
/**
* @title Token predicate interface for all POS portal predicates.
* 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 @animoca/ethereum-contracts-core-1.1.1/contracts/utils/RLPReader.sol@v1.1.1
/*
* @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.7.6 <0.8.0;
library RLPReader {
uint8 private constant _STRING_SHORT_START = 0x80;
uint8 private constant _STRING_LONG_START = 0xb8;
uint8 private constant _LIST_SHORT_START = 0xc0;
uint8 private constant _LIST_LONG_START = 0xf8;
uint8 private constant _WORD_SIZE = 32;
struct RLPItem {
uint256 len;
uint256 memPtr;
}
/*
* @param item RLP encoded bytes
*/
function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
require(item.length > 0, "RLPReader: INVALID_BYTES_LENGTH");
uint256 memPtr;
assembly {
memPtr := add(item, 0x20)
}
return RLPItem(item.length, memPtr);
}
/*
* @param item RLP encoded list in bytes
*/
function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
require(isList(item), "RLP: ITEM_NOT_LIST");
uint256 items = numItems(item);
RLPItem[] memory result = new RLPItem[](items);
uint256 listLength = _itemLength(item.memPtr);
require(listLength == item.len, "RLP: LIST_LENGTH_MISMATCH");
uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
uint256 dataLen;
for (uint256 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) {
uint8 byte0;
uint256 memPtr = item.memPtr;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < _LIST_SHORT_START) return false;
return true;
}
/** RLPItem conversions into data types **/
// @returns raw rlp encoding in bytes
function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
bytes memory result = new bytes(item.len);
uint256 ptr;
assembly {
ptr := add(0x20, result)
}
copy(item.memPtr, ptr, item.len);
return result;
}
function toAddress(RLPItem memory item) internal pure returns (address) {
require(!isList(item), "RLP: DECODING_LIST_AS_ADDRESS");
// 1 byte for the length prefix
require(item.len == 21, "RLP: INVALID_ADDRESS_LEN");
return address(toUint(item));
}
function toUint(RLPItem memory item) internal pure returns (uint256) {
require(!isList(item), "RLP: DECODING_LIST_AS_UINT");
require(item.len <= 33, "RLP: INVALID_UINT_LEN");
uint256 itemLength = _itemLength(item.memPtr);
require(itemLength == item.len, "RLP: UINT_LEN_MISMATCH");
uint256 offset = _payloadOffset(item.memPtr);
uint256 len = item.len - offset;
uint256 result;
uint256 memPtr = item.memPtr + offset;
assembly {
result := mload(memPtr)
// shfit to the correct location if neccesary
if lt(len, 32) {
result := div(result, exp(256, sub(32, len)))
}
}
return result;
}
// enforces 32 byte length
function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
uint256 itemLength = _itemLength(item.memPtr);
require(itemLength == item.len, "RLP: UINT_STRICT_LEN_MISMATCH");
// one byte prefix
require(item.len == 33, "RLP: INVALID_UINT_STRICT_LEN");
uint256 result;
uint256 memPtr = item.memPtr + 1;
assembly {
result := mload(memPtr)
}
return result;
}
function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
uint256 listLength = _itemLength(item.memPtr);
require(listLength == item.len, "RLP: BYTES_LEN_MISMATCH");
uint256 offset = _payloadOffset(item.memPtr);
uint256 len = item.len - offset; // data length
bytes memory result = new bytes(len);
uint256 destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(item.memPtr + offset, destPtr, len);
return result;
}
/*
* Private Helpers
*/
// @return number of payload items inside an encoded list.
function numItems(RLPItem memory item) private pure returns (uint256) {
// add `isList` check if `item` is expected to be passsed without a check from calling function
// require(isList(item), "RLPReader: NUM_ITEMS_NOT_LIST");
uint256 count = 0;
uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
uint256 endPtr = item.memPtr + item.len;
while (currPtr < endPtr) {
currPtr = currPtr + _itemLength(currPtr); // skip over an item
require(currPtr <= endPtr, "RLP: NUM_ITEMS_LEN_MISMATCH");
count++;
}
return count;
}
// @return entire rlp item byte length
function _itemLength(uint256 memPtr) private pure returns (uint256) {
uint256 itemLen;
uint256 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(uint256 memPtr) private pure returns (uint256) {
uint256 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(
uint256 src,
uint256 dest,
uint256 len
) private pure {
if (len == 0) return;
// copy as many word sizes as possible
for (; len >= _WORD_SIZE; len -= _WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += _WORD_SIZE;
dest += _WORD_SIZE;
}
// left over bytes. Mask is used to remove unwanted bytes from the word
uint256 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/bridging/ERC20BasePredicate.sol
pragma solidity >=0.7.6 <0.8.0;
/**
* Polygon (MATIC) bridging base ERC20 predicate to be deployed on the root chain (Ethereum mainnet).
*/
abstract contract ERC20BasePredicate is ITokenPredicate {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
event LockedERC20(address indexed depositor, address indexed depositReceiver, address indexed rootToken, uint256 amount);
bytes32 public constant WITHDRAWN_EVENT_SIG = 0x7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5;
// see https://github.com/maticnetwork/pos-portal/blob/master/contracts/root/RootChainManager/RootChainManager.sol
address public rootChainManager;
/**
* Constructor
* @param rootChainManager_ the Polygon/MATIC RootChainManager proxy address.
*/
constructor(address rootChainManager_) {
rootChainManager = rootChainManager_;
}
function _requireManagerRole(address account) internal view {
require(account == rootChainManager, "Predicate: only manager");
}
function _verifyWithdrawalLog(bytes memory log) internal pure returns (address withdrawer, uint256 amount) {
RLPReader.RLPItem[] memory logRLPList = log.toRlpItem().toList();
RLPReader.RLPItem[] memory logTopicRLPList = logRLPList[1].toList(); // topics
require(
bytes32(logTopicRLPList[0].toUint()) == WITHDRAWN_EVENT_SIG, // topic0 is event sig
"Predicate: invalid signature"
);
bytes memory logData = logRLPList[2].toBytes();
(withdrawer, amount) = abi.decode(logData, (address, uint256));
}
}
// File contracts/bridging/ERC20EscrowPredicate.sol
pragma solidity >=0.7.6 <0.8.0;
/**
* Polygon (MATIC) bridging ERC20 escrowing predicate to be deployed on the root chain (Ethereum mainnet).
* This predicate must be used for non-mintable/non-burnable tokens.
*/
contract ERC20EscrowPredicate is ERC20BasePredicate, ManagedIdentity {
using ERC20Wrapper for IWrappedERC20;
constructor(address rootChainManager_) ERC20BasePredicate(rootChainManager_) {}
/**
* Locks ERC20 tokens for deposit.
* @dev Reverts if not called by the manager (RootChainManager).
* @param depositor Address who wants to deposit tokens.
* @param depositReceiver Address (address) who wants to receive tokens on child chain.
* @param rootToken Token which gets deposited.
* @param depositData ABI encoded amount.
*/
function lockTokens(
address depositor,
address depositReceiver,
address rootToken,
bytes calldata depositData
) external override {
_requireManagerRole(_msgSender());
uint256 amount = abi.decode(depositData, (uint256));
emit LockedERC20(depositor, depositReceiver, rootToken, amount);
IWrappedERC20(rootToken).wrappedTransferFrom(depositor, address(this), amount);
}
/**
* Validates the {Withdrawn} log signature, then sends the correct amount to withdrawer.
* @dev Reverts if not called only by the manager (RootChainManager).
* @param rootToken Token which gets withdrawn
* @param log Valid ERC20 burn log from child chain
*/
function exitTokens(
address,
address rootToken,
bytes memory log
) public override {
_requireManagerRole(_msgSender());
(address withdrawer, uint256 amount) = _verifyWithdrawalLog(log);
IWrappedERC20(rootToken).wrappedTransfer(withdrawer, amount);
}
}
{
"compilationTarget": {
"ERC20EscrowPredicate.sol": "ERC20EscrowPredicate"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 2000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"rootChainManager_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"depositReceiver","type":"address"},{"indexed":true,"internalType":"address","name":"rootToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LockedERC20","type":"event"},{"inputs":[],"name":"WITHDRAWN_EVENT_SIG","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"rootToken","type":"address"},{"internalType":"bytes","name":"log","type":"bytes"}],"name":"exitTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"},{"internalType":"address","name":"depositReceiver","type":"address"},{"internalType":"address","name":"rootToken","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"lockTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rootChainManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]