Contract Address Details
contract

0x0525b5CcA48Fa21CAfE1Db9bd2aA7a4e53c32fb7

Contract Name
HomeNFTOmnibridge
Creator
0xbf3d6f–442b53 at 0x2252f3–008b11
Balance
0 POA
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
24011329
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
HomeNFTOmnibridge




Optimization enabled
true
Compiler version
v0.7.5+commit.eb77ed08




Optimization runs
200
EVM Version
istanbul




Verified at
2021-06-30 20:08:00.023997Z

Contract source code

pragma solidity 0.7.5;
// solhint-disable-next-line compiler-version
pragma abicoder v2;
import "./modules/forwarding_rules/NFTForwardingRulesConnector.sol";
import "./modules/gas_limit/SelectorTokenGasLimitConnector.sol";
/**
* @title HomeNFTOmnibridge
* @dev Home side implementation for multi-token ERC721 mediator intended to work on top of AMB bridge.
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
*/
contract HomeNFTOmnibridge is NFTForwardingRulesConnector, SelectorTokenGasLimitConnector {
constructor(string memory _suffix) BasicNFTOmnibridge(_suffix) {}
/**
* @dev Stores the initial parameters of the mediator.
* @param _bridgeContract the address of the AMB bridge contract.
* @param _mediatorContract the address of the mediator contract on the other network.
* @param _gasLimitManager the gas limit manager contract address.
* @param _owner address of the owner of the mediator contract.
* @param _imageERC721 address of the ERC721 token image.
* @param _imageERC1155 address of the ERC1155 token image.
* @param _forwardingRulesManager address of the NFTForwardingRulesManager contract that will be used for managing lane permissions.
*/
function initialize(
address _bridgeContract,
address _mediatorContract,
address _gasLimitManager,
address _owner,
address _imageERC721,
address _imageERC1155,
address _forwardingRulesManager
) external onlyRelevantSender returns (bool) {
require(!isInitialized());
_setBridgeContract(_bridgeContract);
_setMediatorContractOnOtherSide(_mediatorContract);
_setGasLimitManager(_gasLimitManager);
_setOwner(_owner);
_setTokenImageERC721(_imageERC721);
_setTokenImageERC1155(_imageERC1155);
_setForwardingRulesManager(_forwardingRulesManager);
setInitialize();
return isInitialized();
}
/**
* @dev Internal function for sending an AMB message to the mediator on the other side.
* @param _data data to be sent to the other side of the bridge.
* @param _useOracleLane true, if the message should be sent to the oracle driven lane.
* @return id of the sent message.
*/
function _passMessage(bytes memory _data, bool _useOracleLane) internal override returns (bytes32) {
address executor = mediatorContractOnOtherSide();
uint256 gasLimit = _chooseRequestGasLimit(_data);
IAMB bridge = bridgeContract();
return
_useOracleLane
? bridge.requireToPassMessage(executor, _data, gasLimit)
: bridge.requireToConfirmMessage(executor, _data, gasLimit);
}
// Selector: 0x7f083f69
function migrationTo3_0_0() external {
bytes32 migratedTo3_0_0Storage = 0xa7447530d8540da22352ef04bc2e39e62a1d14ccad54fda9de9532776a4f3201; // keccak256(abi.encodePacked("migrationTo3_0_020210630"))
require(!boolStorage[migratedTo3_0_0Storage]);
_setTokenImageERC721(0xD3b93549686C857EDb0d97B7037bD7D1314c268a);
_setTokenImageERC1155(0x2419697793FA7a582893792a6F9C7391337a8784);
boolStorage[migratedTo3_0_0Storage] = true;
}
}

BasicNFTOmnibridge.sol

pragma solidity 0.7.5;
// solhint-disable-next-line compiler-version
pragma abicoder v2;
import "../Initializable.sol";
import "../Upgradeable.sol";
import "../../interfaces/IBurnableMintableERC1155Token.sol";
import "./components/common/BridgeOperationsStorage.sol";
import "./components/common/FailedMessagesProcessor.sol";
import "./components/common/NFTBridgeLimits.sol";
import "./components/common/ERC721Relayer.sol";
import "./components/common/ERC1155Relayer.sol";
import "./components/common/NFTOmnibridgeInfo.sol";
import "./components/native/NativeTokensRegistry.sol";
import "./components/native/MetadataReader.sol";
import "./components/bridged/BridgedTokensRegistry.sol";
import "./components/bridged/TokenImageStorage.sol";
import "./components/bridged/ERC721TokenProxy.sol";
import "./components/bridged/ERC1155TokenProxy.sol";
import "./components/native/NFTMediatorBalanceStorage.sol";
import "../../tokens/ERC721BridgeToken.sol";
/**
* @title BasicNFTOmnibridge
* @dev Commong functionality for multi-token mediator for ERC721 tokens intended to work on top of AMB bridge.
*/
abstract contract BasicNFTOmnibridge is
Initializable,
Upgradeable,
BridgeOperationsStorage,
BridgedTokensRegistry,
NativeTokensRegistry,
NFTOmnibridgeInfo,
NFTBridgeLimits,
MetadataReader,
TokenImageStorage,
ERC721Relayer,
ERC1155Relayer,
NFTMediatorBalanceStorage,
FailedMessagesProcessor
{
using SafeMath for uint256;
// Workaround for storing variable up-to-32 bytes suffix
uint256 private immutable SUFFIX_SIZE;
bytes32 private immutable SUFFIX;
// Since contract is intended to be deployed under EternalStorageProxy, only constant and immutable variables can be set here
constructor(string memory _suffix) {
require(bytes(_suffix).length <= 32);
bytes32 suffix;
assembly {
suffix := mload(add(_suffix, 32))
}
SUFFIX = suffix;
SUFFIX_SIZE = bytes(_suffix).length;
}
/**
* @dev Checks if specified token was already bridged at least once and it is registered in the Omnibridge.
* @param _token address of the token contract.
* @return true, if token was already bridged.
*/
function isTokenRegistered(address _token) public view override returns (bool) {
return isRegisteredAsNativeToken(_token) || nativeTokenAddress(_token) != address(0);
}
/**
* @dev Handles the bridged token for the first time, includes deployment of new ERC721TokenProxy contract.
* @param _token address of the native ERC721 token on the other side.
* @param _name name of the native token, name suffix will be appended, if empty, symbol will be used instead.
* @param _symbol symbol of the bridged token, if empty, name will be used instead.
* @param _recipient address that will receive the tokens.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
* @param _tokenURIs URIs for the bridged token instances.
*/
function deployAndHandleBridgedNFT(
address _token,
string memory _name,
string memory _symbol,
address _recipient,
uint256[] calldata _tokenIds,
uint256[] calldata _values,
string[] calldata _tokenURIs
) external onlyMediator {
address bridgedToken = bridgedTokenAddress(_token);
if (bridgedToken == address(0)) {
if (bytes(_name).length == 0) {
if (bytes(_symbol).length > 0) {
_name = _transformName(_symbol);
}
} else {
if (bytes(_symbol).length == 0) {
_symbol = _name;
}
_name = _transformName(_name);
}
bridgedToken = _values.length > 0
? address(new ERC1155TokenProxy(tokenImageERC1155(), _name, _symbol, address(this)))
: address(new ERC721TokenProxy(tokenImageERC721(), _name, _symbol, address(this)));
_setTokenAddressPair(_token, bridgedToken);
}
_handleTokens(bridgedToken, false, _recipient, _tokenIds, _values);
_setTokensURI(bridgedToken, _tokenIds, _tokenURIs);
}
/**
* @dev Handles the bridged token for the already registered token pair.
* Checks that the bridged token is inside the execution limits and invokes the Mint accordingly.
* @param _token address of the native ERC721 token on the other side.
* @param _recipient address that will receive the tokens.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
* @param _tokenURIs URIs for the bridged token instances.
*/
function handleBridgedNFT(
address _token,
address _recipient,
uint256[] calldata _tokenIds,
uint256[] calldata _values,
string[] calldata _tokenURIs
) external onlyMediator {
address token = bridgedTokenAddress(_token);
_handleTokens(token, false, _recipient, _tokenIds, _values);
_setTokensURI(token, _tokenIds, _tokenURIs);
}
/**
* @dev Handles the bridged token that are native to this chain.
* Checks that the bridged token is inside the execution limits and invokes the Unlock accordingly.
* @param _token address of the native ERC721 token contract.
* @param _recipient address that will receive the tokens.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function handleNativeNFT(
address _token,
address _recipient,
uint256[] calldata _tokenIds,
uint256[] calldata _values
) external onlyMediator {
require(isRegisteredAsNativeToken(_token));
_setNativeTokenIsRegistered(_token, REGISTERED_AND_DEPLOYED);
_handleTokens(_token, true, _recipient, _tokenIds, _values);
}
/**
* @dev Allows to pre-set the bridged token contract for not-yet bridged token.
* Only the owner can call this method.
* @param _nativeToken address of the token contract on the other side that was not yet bridged.
* @param _bridgedToken address of the bridged token contract.
*/
function setCustomTokenAddressPair(address _nativeToken, address _bridgedToken) external onlyOwner {
require(Address.isContract(_bridgedToken));
require(!isTokenRegistered(_bridgedToken));
require(bridgedTokenAddress(_nativeToken) == address(0));
// Unfortunately, there is no simple way to verify that the _nativeToken address
// does not belong to the bridged token on the other side,
// since information about bridged tokens addresses is not transferred back.
// Therefore, owner account calling this function SHOULD manually verify on the other side of the bridge that
// nativeTokenAddress(_nativeToken) == address(0) && isTokenRegistered(_nativeToken) == false.
_setTokenAddressPair(_nativeToken, _bridgedToken);
}
/**
* @dev Allows to send to the other network some ERC721 token that can be forced into the contract
* without the invocation of the required methods. (e. g. regular transferFrom without a call to onERC721Received)
* Before calling this method, it must be carefully investigated how imbalance happened
* in order to avoid an attempt to steal the funds from a token with double addresses.
* @param _token address of the token contract.
* @param _receiver the address that will receive the token on the other network.
* @param _tokenIds unique ids of the bridged tokens.
*/
function fixMediatorBalanceERC721(
address _token,
address _receiver,
uint256[] calldata _tokenIds
) external onlyIfUpgradeabilityOwner {
require(isRegisteredAsNativeToken(_token));
require(_tokenIds.length > 0);
uint256[] memory values = new uint256[](0);
bytes memory data = _prepareMessage(_token, _receiver, _tokenIds, values);
bytes32 _messageId = _passMessage(data, true);
_recordBridgeOperation(_messageId, _token, _receiver, _tokenIds, values);
}
/**
* @dev Allows to send to the other network some ERC1155 token that can be forced into the contract
* without the invocation of the required methods.
* Before calling this method, it must be carefully investigated how imbalance happened
* in order to avoid an attempt to steal the funds from a token with double addresses.
* @param _token address of the token contract.
* @param _receiver the address that will receive the token on the other network.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values corresponding amounts of the bridged tokens.
*/
function fixMediatorBalanceERC1155(
address _token,
address _receiver,
uint256[] calldata _tokenIds,
uint256[] calldata _values
) external onlyIfUpgradeabilityOwner {
require(isRegisteredAsNativeToken(_token));
require(_tokenIds.length == _values.length);
require(_tokenIds.length > 0);
bytes memory data = _prepareMessage(_token, _receiver, _tokenIds, _values);
bytes32 _messageId = _passMessage(data, true);
_recordBridgeOperation(_messageId, _token, _receiver, _tokenIds, _values);
}
/**
* @dev Executes action on deposit of ERC721 token.
* @param _token address of the ERC721 token contract.
* @param _from address of token sender.
* @param _receiver address of token receiver on the other side.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function bridgeSpecificActionsOnTokenTransfer(
address _token,
address _from,
address _receiver,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal override {
if (!isTokenRegistered(_token)) {
_setNativeTokenIsRegistered(_token, REGISTERED);
}
bytes memory data = _prepareMessage(_token, _receiver, _tokenIds, _values);
bytes32 _messageId = _passMessage(data, _isOracleDrivenLaneAllowed(_token, _from, _receiver));
_recordBridgeOperation(_messageId, _token, _from, _tokenIds, _values);
}
/**
* @dev Constructs the message to be sent to the other side. Burns/locks bridged token.
* @param _token bridged token address.
* @param _receiver address of the tokens receiver on the other side.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function _prepareMessage(
address _token,
address _receiver,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal returns (bytes memory) {
require(_receiver != address(0) && _receiver != mediatorContractOnOtherSide());
address nativeToken = nativeTokenAddress(_token);
// process token is native with respect to this side of the bridge
if (nativeToken == address(0)) {
string[] memory tokenURIs = new string[](_tokenIds.length);
if (_values.length > 0) {
for (uint256 i = 0; i < _tokenIds.length; i++) {
uint256 oldBalance = mediatorOwns(_token, _tokenIds[i]);
uint256 newBalance = oldBalance.add(_values[i]);
require(IERC1155(_token).balanceOf(address(this), _tokenIds[i]) >= newBalance);
_setMediatorOwns(_token, _tokenIds[i], newBalance);
tokenURIs[i] = _readERC1155TokenURI(_token, _tokenIds[i]);
}
} else {
for (uint256 i = 0; i < _tokenIds.length; i++) {
require(mediatorOwns(_token, _tokenIds[i]) == 0);
require(IERC721(_token).ownerOf(_tokenIds[i]) == address(this));
_setMediatorOwns(_token, _tokenIds[i], 1);
tokenURIs[i] = _readERC721TokenURI(_token, _tokenIds[i]);
}
}
// process token which bridged alternative was already ACKed to be deployed
if (isBridgedTokenDeployAcknowledged(_token)) {
require(_tokenIds.length <= MAX_BATCH_BRIDGE_LIMIT);
return
abi.encodeWithSelector(
this.handleBridgedNFT.selector,
_token,
_receiver,
_tokenIds,
_values,
tokenURIs
);
}
require(_tokenIds.length <= MAX_BATCH_BRIDGE_AND_DEPLOY_LIMIT);
string memory name = _readName(_token);
string memory symbol = _readSymbol(_token);
return
abi.encodeWithSelector(
this.deployAndHandleBridgedNFT.selector,
_token,
name,
symbol,
_receiver,
_tokenIds,
_values,
tokenURIs
);
}
// process already known token that is bridged from other chain
if (_values.length > 0) {
IBurnableMintableERC1155Token(_token).burn(_tokenIds, _values);
} else {
for (uint256 i = 0; i < _tokenIds.length; i++) {
IBurnableMintableERC721Token(_token).burn(_tokenIds[i]);
}
}
return abi.encodeWithSelector(this.handleNativeNFT.selector, nativeToken, _receiver, _tokenIds, _values);
}
/**
* @dev Unlock/Mint back the bridged token that was bridged to the other network but failed.
* @param _token address that bridged token contract.
* @param _recipient address that will receive the tokens.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function executeActionOnFixedTokens(
address _token,
address _recipient,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal override {
_releaseTokens(_token, nativeTokenAddress(_token) == address(0), _recipient, _tokenIds, _values);
}
/**
* @dev Handles the bridged token that came from the other side of the bridge.
* Checks that the operation is inside the execution limits and invokes the Mint or Unlock accordingly.
* @param _token token contract address on this side of the bridge.
* @param _isNative true, if given token is native to this chain and Unlock should be used.
* @param _recipient address that will receive the tokens.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function _handleTokens(
address _token,
bool _isNative,
address _recipient,
uint256[] calldata _tokenIds,
uint256[] calldata _values
) internal {
require(isTokenExecutionAllowed(_token));
_releaseTokens(_token, _isNative, _recipient, _tokenIds, _values);
emit TokensBridged(_token, _recipient, _tokenIds, _values, messageId());
}
/**
* Internal function for setting token URI for the bridged token instance.
* @param _token address of the token contract.
* @param _tokenIds unique ids of the bridged tokens.
* @param _tokenURIs URIs for the bridged token instances.
*/
function _setTokensURI(
address _token,
uint256[] calldata _tokenIds,
string[] calldata _tokenURIs
) internal {
for (uint256 i = 0; i < _tokenIds.length; i++) {
if (bytes(_tokenURIs[i]).length > 0) {
IBurnableMintableERC721Token(_token).setTokenURI(_tokenIds[i], _tokenURIs[i]);
}
}
}
/**
* Internal function for unlocking/minting some specific ERC721 token.
* @param _token address of the token contract.
* @param _isNative true, if the token contract is native w.r.t to the bridge.
* @param _recipient address of the tokens receiver.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function _releaseTokens(
address _token,
bool _isNative,
address _recipient,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal {
if (_values.length > 0) {
if (_isNative) {
for (uint256 i = 0; i < _tokenIds.length; i++) {
_setMediatorOwns(_token, _tokenIds[i], mediatorOwns(_token, _tokenIds[i]).sub(_values[i]));
}
IERC1155(_token).safeBatchTransferFrom(address(this), _recipient, _tokenIds, _values, new bytes(0));
} else {
IBurnableMintableERC1155Token(_token).mint(_recipient, _tokenIds, _values);
}
} else {
if (_isNative) {
for (uint256 i = 0; i < _tokenIds.length; i++) {
_setMediatorOwns(_token, _tokenIds[i], 0);
IERC721(_token).transferFrom(address(this), _recipient, _tokenIds[i]);
}
} else {
for (uint256 i = 0; i < _tokenIds.length; i++) {
IBurnableMintableERC721Token(_token).mint(_recipient, _tokenIds[i]);
}
}
}
}
/**
* @dev Internal function for recording bridge operation for further usage.
* Recorded information is used for fixing failed requests on the other side.
* @param _messageId id of the sent message.
* @param _token bridged token address.
* @param _sender address of the tokens sender.
* @param _tokenIds unique ids of the bridged tokens.
* @param _values amounts of bridged tokens. Should be empty list for ERC721.
*/
function _recordBridgeOperation(
bytes32 _messageId,
address _token,
address _sender,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal {
require(isTokenBridgingAllowed(_token));
setMessageChecksum(_messageId, _messageChecksum(_token, _sender, _tokenIds, _values));
emit TokensBridgingInitiated(_token, _sender, _tokenIds, _values, _messageId);
}
/**
* @dev Checks if bridge operation is allowed to use oracle driven lane.
* @param _token address of the token contract on the foreign side of the bridge.
* @param _sender address of the tokens sender on the home side of the bridge.
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
* @return true, if message can be forwarded to the oracle-driven lane.
*/
function _isOracleDrivenLaneAllowed(
address _token,
address _sender,
address _receiver
) internal view virtual returns (bool) {
(_token, _sender, _receiver);
return true;
}
/**
* @dev Internal function for transforming the bridged token name. Appends a side-specific suffix.
* @param _name bridged token from the other side.
* @return token name for this side of the bridge.
*/
function _transformName(string memory _name) internal view returns (string memory) {
string memory result = string(abi.encodePacked(_name, SUFFIX));
uint256 size = SUFFIX_SIZE;
assembly {
mstore(result, add(mload(_name), size))
}
return result;
}
}

VersionableBridge.sol

pragma solidity 0.7.5;
interface VersionableBridge {
function getBridgeInterfacesVersion()
external
pure
returns (
uint64 major,
uint64 minor,
uint64 patch
);
function getBridgeMode() external pure returns (bytes4);
}

IUpgradeabilityOwnerStorage.sol

pragma solidity 0.7.5;
interface IUpgradeabilityOwnerStorage {
function upgradeabilityOwner() external view returns (address);
}

IOwnable.sol

pragma solidity 0.7.5;
interface IOwnable {
function owner() external view returns (address);
}

Upgradeable.sol

pragma solidity 0.7.5;
import "../interfaces/IUpgradeabilityOwnerStorage.sol";
contract Upgradeable {
// Avoid using onlyUpgradeabilityOwner name to prevent issues with implementation from proxy contract
modifier onlyIfUpgradeabilityOwner() {
require(msg.sender == IUpgradeabilityOwnerStorage(address(this)).upgradeabilityOwner());
_;
}
}

SelectorTokenGasLimitManager.sol

pragma solidity 0.7.5;
import "../../../../interfaces/IAMB.sol";
import "../../BasicNFTOmnibridge.sol";
import "../OmnibridgeModule.sol";
/**
* @title SelectorTokenGasLimitManager
* @dev Multi NFT mediator functionality for managing request gas limits.
*/
contract SelectorTokenGasLimitManager is OmnibridgeModule {
IAMB public bridge;
uint256 internal defaultGasLimit;
mapping(bytes4 => uint256) internal selectorGasLimit;
mapping(bytes4 => mapping(address => uint256)) internal selectorTokenGasLimit;
/**
* @dev Initializes this module contract. Intended to be called only once through the proxy pattern.
* @param _bridge address of the AMB bridge contract to which Omnibridge mediator is connected.
* @param _mediator address of the Omnibridge contract working with this module.
* @param _gasLimit default gas limit for the message execution.
*/
function initialize(
IAMB _bridge,
IOwnable _mediator,
uint256 _gasLimit
) external {
require(address(mediator) == address(0));
require(_gasLimit <= _bridge.maxGasPerTx());
mediator = _mediator;
bridge = _bridge;
defaultGasLimit = _gasLimit;
}
/**
* @dev Tells the module interface version that this contract supports.
* @return major value of the version
* @return minor value of the version
* @return patch value of the version
*/
function getModuleInterfacesVersion()
external
pure
override
returns (
uint64 major,
uint64 minor,
uint64 patch
)
{
return (1, 0, 0);
}
/**
* @dev Throws if provided gas limit is greater then the maximum allowed gas limit in the AMB contract.
* @param _gasLimit gas limit value to check.
*/
modifier validGasLimit(uint256 _gasLimit) {
require(_gasLimit <= bridge.maxGasPerTx());
_;
}
/**
* @dev Throws if one of the provided gas limits is greater then the maximum allowed gas limit in the AMB contract.
* @param _length expected length of the _gasLimits array.
* @param _gasLimits array of gas limit values to check, should contain exactly _length elements.
*/
modifier validGasLimits(uint256 _length, uint256[] calldata _gasLimits) {
require(_gasLimits.length == _length);
uint256 maxGasLimit = bridge.maxGasPerTx();
for (uint256 i = 0; i < _length; i++) {
require(_gasLimits[i] <= maxGasLimit);
}
_;
}
/**
* @dev Sets the default gas limit to be used in the message execution by the AMB bridge on the other network.
* This value can't exceed the parameter maxGasPerTx defined on the AMB bridge.
* Only the owner can call this method.
* @param _gasLimit the gas limit for the message execution.
*/
function setRequestGasLimit(uint256 _gasLimit) external onlyOwner validGasLimit(_gasLimit) {
defaultGasLimit = _gasLimit;
}
/**
* @dev Sets the selector-specific gas limit to be used in the message execution by the AMB bridge on the other network.
* This value can't exceed the parameter maxGasPerTx defined on the AMB bridge.
* Only the owner can call this method.
* @param _selector method selector of the outgoing message payload.
* @param _gasLimit the gas limit for the message execution.
*/
function setRequestGasLimit(bytes4 _selector, uint256 _gasLimit) external onlyOwner validGasLimit(_gasLimit) {
selectorGasLimit[_selector] = _gasLimit;
}
/**
* @dev Sets the token-specific gas limit to be used in the message execution by the AMB bridge on the other network.
* This value can't exceed the parameter maxGasPerTx defined on the AMB bridge.
* Only the owner can call this method.
* @param _selector method selector of the outgoing message payload.
* @param _token address of the native token that is used in the first argument of handleBridgedTokens/handleNativeTokens.
* @param _gasLimit the gas limit for the message execution.
*/
function setRequestGasLimit(
bytes4 _selector,
address _token,
uint256 _gasLimit
) external onlyOwner validGasLimit(_gasLimit) {
selectorTokenGasLimit[_selector][_token] = _gasLimit;
}
/**
* @dev Tells the default gas limit to be used in the message execution by the AMB bridge on the other network.
* @return the gas limit for the message execution.
*/
function requestGasLimit() public view returns (uint256) {
return defaultGasLimit;
}
/**
* @dev Tells the selector-specific gas limit to be used in the message execution by the AMB bridge on the other network.
* @param _selector method selector for the passed message.
* @return the gas limit for the message execution.
*/
function requestGasLimit(bytes4 _selector) public view returns (uint256) {
return selectorGasLimit[_selector];
}
/**
* @dev Tells the token-specific gas limit to be used in the message execution by the AMB bridge on the other network.
* @param _selector method selector for the passed message.
* @param _token address of the native token that is used in the first argument of handleBridgedTokens/handleNativeTokens.
* @return the gas limit for the message execution.
*/
function requestGasLimit(bytes4 _selector, address _token) public view returns (uint256) {
return selectorTokenGasLimit[_selector][_token];
}
/**
* @dev Tells the gas limit to use for the message execution by the AMB bridge on the other network.
* @param _data calldata to be used on the other side of the bridge, when execution a message.
* @return the gas limit for the message execution.
*/
function requestGasLimit(bytes memory _data) external view returns (uint256) {
bytes4 selector;
address token;
assembly {
// first 4 bytes of _data contain the selector of the function to be called on the other side of the bridge.
// mload(add(_data, 4)) loads selector to the 28-31 bytes of the word.
// shl(28 * 8, x) then used to correct the padding of the selector, putting it to 0-3 bytes of the word.
selector := shl(224, mload(add(_data, 4)))
// handleBridgedTokens/handleNativeTokens/... passes bridged token address as the first parameter.
// it is located in the 4-35 bytes of the calldata.
// 36 = bytes length padding (32) + selector length (4)
token := mload(add(_data, 36))
}
uint256 gasLimit = selectorTokenGasLimit[selector][token];
if (gasLimit == 0) {
gasLimit = selectorGasLimit[selector];
if (gasLimit == 0) {
gasLimit = defaultGasLimit;
}
}
return gasLimit;
}
/**
* @dev Sets the default values for different NFT Omnibridge selectors.
* @param _gasLimits array with 4 gas limits for the following selectors of the outgoing messages:
* - deployAndHandleBridgedNFT
* - handleBridgedNFT
* - handleNativeNFT
* - fixFailedMessage
* Only the owner can call this method.
*/
function setCommonRequestGasLimits(uint256[] calldata _gasLimits) external onlyOwner validGasLimits(4, _gasLimits) {
require(_gasLimits[0] >= _gasLimits[1]);
selectorGasLimit[BasicNFTOmnibridge.deployAndHandleBridgedNFT.selector] = _gasLimits[0];
selectorGasLimit[BasicNFTOmnibridge.handleBridgedNFT.selector] = _gasLimits[1];
selectorGasLimit[BasicNFTOmnibridge.handleNativeNFT.selector] = _gasLimits[2];
selectorGasLimit[FailedMessagesProcessor.fixFailedMessage.selector] = _gasLimits[3];
}
/**
* @dev Sets the request gas limits for some specific token bridged from Foreign side of the bridge.
* @param _token address of the native token contract on the Foreign side.
* @param _gasLimits array with 1 gas limit for the following selectors of the outgoing messages:
* - handleNativeNFT
* Only the owner can call this method.
*/
function setBridgedTokenRequestGasLimits(address _token, uint256[] calldata _gasLimits)
external
onlyOwner
validGasLimits(1, _gasLimits)
{
selectorTokenGasLimit[BasicNFTOmnibridge.handleNativeNFT.selector][_token] = _gasLimits[0];
}
/**
* @dev Sets the request gas limits for some specific token native to the Home side of the bridge.
* @param _token address of the native token contract on the Home side.
* @param _gasLimits array with 2 gas limits for the following selectors of the outgoing messages:
* - deployAndHandleBridgedNFT
* - handleBridgedNFT
* Only the owner can call this method.
*/
function setNativeTokenRequestGasLimits(address _token, uint256[] calldata _gasLimits)
external
onlyOwner
validGasLimits(2, _gasLimits)
{
require(_gasLimits[0] >= _gasLimits[1]);
selectorTokenGasLimit[BasicNFTOmnibridge.deployAndHandleBridgedNFT.selector][_token] = _gasLimits[0];
selectorTokenGasLimit[BasicNFTOmnibridge.handleBridgedNFT.selector][_token] = _gasLimits[1];
}
}

SelectorTokenGasLimitConnector.sol

pragma solidity 0.7.5;
import "../../../Ownable.sol";
import "./SelectorTokenGasLimitManager.sol";
import "../../../BasicAMBMediator.sol";
/**
* @title SelectorTokenGasLimitConnector
* @dev Connectivity functionality that is required for using gas limit manager.
*/
abstract contract SelectorTokenGasLimitConnector is Ownable, BasicAMBMediator {
bytes32 internal constant GAS_LIMIT_MANAGER_CONTRACT =
0x5f5bc4e0b888be22a35f2166061a04607296c26861006b9b8e089a172696a822; // keccak256(abi.encodePacked("gasLimitManagerContract"))
/**
* @dev Updates an address of the used gas limit manager contract.
* @param _manager address of gas limit manager contract.
*/
function setGasLimitManager(address _manager) external onlyOwner {
_setGasLimitManager(_manager);
}
/**
* @dev Retrieves an address of the gas limit manager contract.
* @return address of the gas limit manager contract.
*/
function gasLimitManager() public view returns (SelectorTokenGasLimitManager) {
return SelectorTokenGasLimitManager(addressStorage[GAS_LIMIT_MANAGER_CONTRACT]);
}
/**
* @dev Internal function for updating an address of the used gas limit manager contract.
* @param _manager address of gas limit manager contract.
*/
function _setGasLimitManager(address _manager) internal {
require(_manager == address(0) || Address.isContract(_manager));
addressStorage[GAS_LIMIT_MANAGER_CONTRACT] = _manager;
}
/**
* @dev Tells the gas limit to use for the message execution by the AMB bridge on the other network.
* @param _data calldata to be used on the other side of the bridge, when execution a message.
* @return the gas limit for the message execution.
*/
function _chooseRequestGasLimit(bytes memory _data) internal view returns (uint256) {
SelectorTokenGasLimitManager manager = gasLimitManager();
return address(manager) == address(0) ? maxGasPerTx() : manager.requestGasLimit(_data);
}
}

NFTForwardingRulesManager.sol

pragma solidity 0.7.5;
import "../OmnibridgeModule.sol";
/**
* @title NFTForwardingRulesManager
* @dev NFT Omnibrdge module for managing destination AMB lanes permissions.
*/
contract NFTForwardingRulesManager is OmnibridgeModule {
address internal constant ANY_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
// Forwarding rules mapping
// token => sender => receiver => destination lane
mapping(address => mapping(address => mapping(address => int256))) public forwardingRule;
event ForwardingRuleUpdated(address token, address sender, address receiver, int256 lane);
/**
* @dev Initializes this module contract. Intended to be called only once through the proxy pattern.
* @param _mediator address of the Omnibridge contract working with this module.
*/
function initialize(IOwnable _mediator) external {
require(address(mediator) == address(0));
mediator = _mediator;
}
/**
* @dev Tells the module interface version that this contract supports.
* @return major value of the version
* @return minor value of the version
* @return patch value of the version
*/
function getModuleInterfacesVersion()
external
pure
override
returns (
uint64 major,
uint64 minor,
uint64 patch
)
{
return (2, 0, 0);
}
/**
* @dev Tells the destination lane for a particular bridge operation by checking several wildcard forwarding rules.
* @param _token address of the token contract on the foreign side of the bridge.
* @param _sender address of the tokens sender on the home side of the bridge.
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
* @return destination lane identifier, where the message should be forwarded to.
* 1 - oracle-driven-lane should be used.
* 0 - default behaviour should be applied.
* -1 - manual lane should be used.
*/
function destinationLane(
address _token,
address _sender,
address _receiver
) public view returns (int256) {
int256 lane = forwardingRule[ANY_ADDRESS][_sender][ANY_ADDRESS]; // all tokens for specific sender
if (lane != 0) return lane;
lane = forwardingRule[ANY_ADDRESS][ANY_ADDRESS][_receiver]; // all tokens for specific receiver
if (lane != 0) return lane;
lane = forwardingRule[_token][ANY_ADDRESS][ANY_ADDRESS]; // specific token for all senders and receivers
if (lane != 0) return lane;
lane = forwardingRule[_token][_sender][ANY_ADDRESS]; // specific token for specific sender
if (lane != 0) return lane;
return forwardingRule[_token][ANY_ADDRESS][_receiver]; // specific token for specific receiver
}
/**
* Updates the forwarding rule for bridging specific token.
* Only owner can call this method.
* @param _token address of the token contract on the foreign side.
* @param _enable true, if bridge operations for a given token should be forwarded to the oracle-driven lane.
*/
function setRuleForTokenToPBO(address _token, bool _enable) external {
require(_token != ANY_ADDRESS);
_setForwardingRule(_token, ANY_ADDRESS, ANY_ADDRESS, _enable ? int256(1) : int256(0));
}
/**
* Allows a particular address to send bridge requests to the oracle-driven lane for a particular token.
* Only owner can call this method.
* @param _token address of the token contract on the foreign side.
* @param _sender address of the tokens sender on the home side of the bridge.
* @param _enable true, if bridge operations for a given token and sender should be forwarded to the oracle-driven lane.
*/
function setRuleForTokenAndSenderToPBO(
address _token,
address _sender,
bool _enable
) external {
require(_token != ANY_ADDRESS);
require(_sender != ANY_ADDRESS);
_setForwardingRule(_token, _sender, ANY_ADDRESS, _enable ? int256(1) : int256(0));
}
/**
* Allows a particular address to receive bridged tokens from the oracle-driven lane for a particular token.
* Only owner can call this method.
* @param _token address of the token contract on the foreign side.
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
* @param _enable true, if bridge operations for a given token and receiver should be forwarded to the oracle-driven lane.
*/
function setRuleForTokenAndReceiverToPBO(
address _token,
address _receiver,
bool _enable
) external {
require(_token != ANY_ADDRESS);
require(_receiver != ANY_ADDRESS);
_setForwardingRule(_token, ANY_ADDRESS, _receiver, _enable ? int256(1) : int256(0));
}
/**
* Updates the forwarding rule for the specific sender.
* Only owner can call this method.
* @param _sender address of the tokens sender on the home side.
* @param _enable true, if all bridge operations from a given sender should be forwarded to the oracle-driven lane.
*/
function setRuleForSenderOfAnyTokenToPBO(address _sender, bool _enable) external {
require(_sender != ANY_ADDRESS);
_setForwardingRule(ANY_ADDRESS, _sender, ANY_ADDRESS, _enable ? int256(1) : int256(0));
}
/**
* Updates the forwarding rule for the specific receiver.
* Only owner can call this method.
* @param _receiver address of the tokens receiver on the foreign side.
* @param _enable true, if all bridge operations to a given receiver should be forwarded to the oracle-driven lane.
*/
function setRuleForReceiverOfAnyTokenToPBO(address _receiver, bool _enable) external {
require(_receiver != ANY_ADDRESS);
_setForwardingRule(ANY_ADDRESS, ANY_ADDRESS, _receiver, _enable ? int256(1) : int256(0));
}
/**
* Updates the forwarding rule for the specific sender.
* Only owner can call this method.
* @param _sender address of the tokens sender on the home side.
* @param _enable true, if all bridge operations from a given sender should be forwarded to the manual lane.
*/
function setRuleForSenderOfAnyTokenToPBU(address _sender, bool _enable) external {
require(_sender != ANY_ADDRESS);
_setForwardingRule(ANY_ADDRESS, _sender, ANY_ADDRESS, _enable ? int256(-1) : int256(0));
}
/**
* Updates the forwarding rule for the specific receiver.
* Only owner can call this method.
* @param _receiver address of the tokens receiver on the foreign side.
* @param _enable true, if all bridge operations to a given receiver should be forwarded to the manual lane.
*/
function setRuleForReceiverOfAnyTokenToPBU(address _receiver, bool _enable) external {
require(_receiver != ANY_ADDRESS);
_setForwardingRule(ANY_ADDRESS, ANY_ADDRESS, _receiver, _enable ? int256(-1) : int256(0));
}
/**
* @dev Internal function for updating the preferred destination lane for the specific wildcard pattern.
* Only owner can call this method.
* Examples:
* _setForwardingRule(tokenA, ANY_ADDRESS, ANY_ADDRESS, -1) - forward all operations on tokenA to the manual lane
* _setForwardingRule(tokenA, Alice, ANY_ADDRESS, 1) - allow Alice to use the oracle-driven lane for bridging tokenA
* _setForwardingRule(tokenA, ANY_ADDRESS, Bob, 1) - forward all tokenA bridge operations, where Bob is the receiver, to the oracle-driven lane
* _setForwardingRule(ANY_ADDRESS, Mallory, ANY_ADDRESS, -1) - forward all bridge operations from Mallory to the manual lane
* @param _token address of the token contract on the foreign side of the bridge.
* @param _sender address of the tokens sender on the home side of the bridge.
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
* @param _lane preferred destination lane for the particular sender.
* 1 - forward to the oracle-driven lane.
* 0 - behaviour is unset, proceed by checking other less-specific rules.
* -1 - manual lane should be used.
*/
function _setForwardingRule(
address _token,
address _sender,
address _receiver,
int256 _lane
) internal onlyOwner {
forwardingRule[_token][_sender][_receiver] = _lane;
emit ForwardingRuleUpdated(_token, _sender, _receiver, _lane);
}
}

NFTForwardingRulesConnector.sol

pragma solidity 0.7.5;
// solhint-disable-next-line compiler-version
pragma abicoder v2;
import "@openzeppelin/contracts/utils/Address.sol";
import "./NFTForwardingRulesManager.sol";
import "../../BasicNFTOmnibridge.sol";
/**
* @title NFTForwardingRulesConnector
* @dev Connectivity functionality that is required for using forwarding rules manager.
*/
abstract contract NFTForwardingRulesConnector is BasicNFTOmnibridge {
bytes32 internal constant FORWARDING_RULES_MANAGER_CONTRACT =
0x5f86f226cd489cc09187d5f5e0adfb94308af0d4ceac482dd8a8adea9d80daf4; // keccak256(abi.encodePacked("forwardingRulesManagerContract"))
/**
* @dev Updates an address of the used forwarding rules manager contract.
* @param _manager address of forwarding rules manager contract.
*/
function setForwardingRulesManager(address _manager) external onlyOwner {
_setForwardingRulesManager(_manager);
}
/**
* @dev Retrieves an address of the forwarding rules manager contract.
* @return address of the forwarding rules manager contract.
*/
function forwardingRulesManager() public view returns (NFTForwardingRulesManager) {
return NFTForwardingRulesManager(addressStorage[FORWARDING_RULES_MANAGER_CONTRACT]);
}
/**
* @dev Internal function for updating an address of the used forwarding rules manager contract.
* @param _manager address of forwarding rules manager contract.
*/
function _setForwardingRulesManager(address _manager) internal {
require(_manager == address(0) || Address.isContract(_manager));
addressStorage[FORWARDING_RULES_MANAGER_CONTRACT] = _manager;
}
/**
* @dev Checks if bridge operation is allowed to use oracle driven lane.
* @param _token address of the token contract on the foreign side of the bridge.
* @param _sender address of the tokens sender on the home side of the bridge.
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
* @return true, if message can be forwarded to the oracle-driven lane.
*/
function _isOracleDrivenLaneAllowed(
address _token,
address _sender,
address _receiver
) internal view override returns (bool) {
NFTForwardingRulesManager manager = forwardingRulesManager();
return address(manager) == address(0) || manager.destinationLane(_token, _sender, _receiver) > 0;
}
}

VersionableModule.sol

pragma solidity 0.7.5;
/**
* @title VersionableModule
* @dev Interface for Omnibridge module versioning.
*/
interface VersionableModule {
function getModuleInterfacesVersion()
external
pure
returns (
uint64 major,
uint64 minor,
uint64 patch
);
}

OmnibridgeModule.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/utils/Address.sol";
import "./VersionableModule.sol";
import "../../../interfaces/IOwnable.sol";
/**
* @title OmnibridgeModule
* @dev Common functionality for Omnibridge extension non-upgradeable module.
*/
abstract contract OmnibridgeModule is VersionableModule {
IOwnable public mediator;
/**
* @dev Throws if sender is not the owner of this contract.
*/
modifier onlyOwner {
require(msg.sender == mediator.owner());
_;
}
}

NativeTokensRegistry.sol

pragma solidity 0.7.5;
import "../../../../upgradeability/EternalStorage.sol";
/**
* @title NativeTokensRegistry
* @dev Functionality for keeping track of registered native tokens.
*/
contract NativeTokensRegistry is EternalStorage {
uint256 internal constant REGISTERED = 1;
uint256 internal constant REGISTERED_AND_DEPLOYED = 2;
/**
* @dev Checks if for a given native token, the deployment of its bridged alternative was already acknowledged.
* @param _token address of native token contract.
* @return true, if bridged token was already deployed.
*/
function isBridgedTokenDeployAcknowledged(address _token) public view returns (bool) {
return uintStorage[keccak256(abi.encodePacked("tokenRegistered", _token))] == REGISTERED_AND_DEPLOYED;
}
/**
* @dev Checks if a given token is a bridged token that is native to this side of the bridge.
* @param _token address of token contract.
* @return message id of the send message.
*/
function isRegisteredAsNativeToken(address _token) public view returns (bool) {
return uintStorage[keccak256(abi.encodePacked("tokenRegistered", _token))] > 0;
}
/**
* @dev Internal function for marking native token as registered.
* @param _token address of the token contract.
* @param _state registration state.
*/
function _setNativeTokenIsRegistered(address _token, uint256 _state) internal {
if (uintStorage[keccak256(abi.encodePacked("tokenRegistered", _token))] != _state) {
uintStorage[keccak256(abi.encodePacked("tokenRegistered", _token))] = _state;
}
}
}

ERC1155Relayer.sol

pragma solidity 0.7.5;
import "../../../../interfaces/IERC1155TokenReceiver.sol";
import "./BaseRelayer.sol";
/**
* @title ERC1155Relayer
* @dev Functionality for bridging multiple ERC1155 tokens to the other side of the bridge.
*/
abstract contract ERC1155Relayer is IERC1155TokenReceiver, BaseRelayer {
// max batch size, so that deployAndHandleBridgedNFT fits in 1.000.000 gas
uint256 internal constant MAX_BATCH_BRIDGE_AND_DEPLOY_LIMIT = 14;
// max batch size, so that handleBridgedNFT fits in 1.000.000 gas
uint256 internal constant MAX_BATCH_BRIDGE_LIMIT = 19;
/**
* @dev ERC1155 transfer callback function.
* @param _from address of token sender.
* @param _tokenId id of the transferred token.
* @param _value amount of received tokens.
* @param _data additional transfer data, can be used for passing alternative receiver address.
*/
function onERC1155Received(
address,
address _from,
uint256 _tokenId,
uint256 _value,
bytes calldata _data
) external override returns (bytes4) {
bridgeSpecificActionsOnTokenTransfer(
msg.sender,
_from,
_chooseReceiver(_from, _data),
_singletonArray(_tokenId),
_singletonArray(_value)
);
return msg.sig;
}
/**
* @dev ERC1155 transfer callback function.
* @param _from address of token sender.
* @param _tokenIds unique ids of the received tokens.
* @param _values amounts of received tokens.
* @param _data additional transfer data, can be used for passing alternative receiver address.
*/
function onERC1155BatchReceived(
address,
address _from,
uint256[] calldata _tokenIds,
uint256[] calldata _values,
bytes calldata _data
) external override returns (bytes4) {
require(_tokenIds.length == _values.length);
require(_tokenIds.length > 0);
bridgeSpecificActionsOnTokenTransfer(msg.sender, _from, _chooseReceiver(_from, _data), _tokenIds, _values);
return msg.sig;
}
}

NFTMediatorBalanceStorage.sol

pragma solidity 0.7.5;
import "../../../../upgradeability/EternalStorage.sol";
/**
* @title NFTMediatorBalanceStorage
* @dev Functionality for storing expected mediator balance for native tokens.
*/
contract NFTMediatorBalanceStorage is EternalStorage {
/**
* @dev Tells amount of owned tokens recorded at this mediator. More strict than regular token.ownerOf()/token.balanceOf() checks,
* since does not take into account forced tokens.
* @param _token address of token contract.
* @param _tokenId id of the new owned token.
* @return amount of owned tokens, 0 or 1 for ERC721 NFTs.
*/
function mediatorOwns(address _token, uint256 _tokenId) public view returns (uint256 amount) {
bytes32 key = _getStorageKey(_token, _tokenId);
assembly {
amount := sload(key)
}
}
/**
* @dev Updates ownership information for the particular token.
* @param _token address of token contract.
* @param _tokenId id of the new owned token.
* @param _value amount of owned tokens, 0 or 1 for ERC721 NFTs.
*/
function _setMediatorOwns(
address _token,
uint256 _tokenId,
uint256 _value
) internal {
bytes32 key = _getStorageKey(_token, _tokenId);
assembly {
sstore(key, _value)
}
}
function _getStorageKey(address _token, uint256 _tokenId) private pure returns (bytes32) {
// same as boolStorage[keccak256(abi.encodePacked("mediatorOwns", _token, _tokenId))]
return keccak256(abi.encodePacked(keccak256(abi.encodePacked("mediatorOwns", _token, _tokenId)), uint256(4)));
}
}

MetadataReader.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155MetadataURI.sol";
import "../../../Ownable.sol";
/**
* @title MetadataReader
* @dev Functionality for reading metadata from ERC721/ERC1155 tokens.
*/
contract MetadataReader is Ownable {
/**
* @dev Sets the custom metadata for the given ERC721/ERC1155 token.
* Only owner can call this method.
* Useful when original NFT token does not implement neither name() nor symbol() methods.
* @param _token address of the token contract.
* @param _name custom name for the token contract.
* @param _symbol custom symbol for the token contract.
*/
function setCustomMetadata(
address _token,
string calldata _name,
string calldata _symbol
) external onlyOwner {
stringStorage[keccak256(abi.encodePacked("customName", _token))] = _name;
stringStorage[keccak256(abi.encodePacked("customSymbol", _token))] = _symbol;
}
/**
* @dev Internal function for reading ERC721/ERC1155 token name.
* Use custom predefined name in case name() function is not implemented.
* @param _token address of the ERC721/ERC1155 token contract.
* @return name for the token.
*/
function _readName(address _token) internal view returns (string memory) {
(bool status, bytes memory data) = _token.staticcall(abi.encodeWithSelector(IERC721Metadata.name.selector));
return status ? abi.decode(data, (string)) : stringStorage[keccak256(abi.encodePacked("customName", _token))];
}
/**
* @dev Internal function for reading ERC721/ERC1155 token symbol.
* Use custom predefined symbol in case symbol() function is not implemented.
* @param _token address of the ERC721/ERC1155 token contract.
* @return symbol for the token.
*/
function _readSymbol(address _token) internal view returns (string memory) {
(bool status, bytes memory data) = _token.staticcall(abi.encodeWithSelector(IERC721Metadata.symbol.selector));
return status ? abi.decode(data, (string)) : stringStorage[keccak256(abi.encodePacked("customSymbol", _token))];
}
/**
* @dev Internal function for reading ERC721 token URI.
* @param _token address of the ERC721 token contract.
* @param _tokenId unique identifier for the token.
* @return token URI for the particular token, if any.
*/
function _readERC721TokenURI(address _token, uint256 _tokenId) internal view returns (string memory) {
(bool status, bytes memory data) =
_token.staticcall(abi.encodeWithSelector(IERC721Metadata.tokenURI.selector, _tokenId));
return status ? abi.decode(data, (string)) : "";
}
/**
* @dev Internal function for reading ERC1155 token URI.
* @param _token address of the ERC1155 token contract.
* @param _tokenId unique identifier for the token.
* @return token URI for the particular token, if any.
*/
function _readERC1155TokenURI(address _token, uint256 _tokenId) internal view returns (string memory) {
(bool status, bytes memory data) =
_token.staticcall(abi.encodeWithSelector(IERC1155MetadataURI.uri.selector, _tokenId));
return status ? abi.decode(data, (string)) : "";
}
}

NFTOmnibridgeInfo.sol

pragma solidity 0.7.5;
import "../../../VersionableBridge.sol";
/**
* @title NFTOmnibridgeInfo
* @dev Functionality for versioning NFTOmnibridge mediator.
*/
contract NFTOmnibridgeInfo is VersionableBridge {
event TokensBridgingInitiated(
address indexed token,
address indexed sender,
uint256[] tokenIds,
uint256[] values,
bytes32 indexed messageId
);
event TokensBridged(
address indexed token,
address indexed recipient,
uint256[] tokenIds,
uint256[] values,
bytes32 indexed messageId
);
/**
* @dev Tells the bridge interface version that this contract supports.
* @return major value of the version
* @return minor value of the version
* @return patch value of the version
*/
function getBridgeInterfacesVersion()
external
pure
override
returns (
uint64 major,
uint64 minor,
uint64 patch
)
{
return (3, 1, 0);
}
/**
* @dev Tells the bridge mode that this contract supports.
* @return _data 4 bytes representing the bridge mode
*/
function getBridgeMode() external pure override returns (bytes4 _data) {
return 0xca7fc3dc; // bytes4(keccak256(abi.encodePacked("multi-nft-to-nft-amb")))
}
}

NFTBridgeLimits.sol

pragma solidity 0.7.5;
import "../../../Ownable.sol";
/**
* @title NFTBridgeLimits
* @dev Functionality for keeping track of bridging limits for multiple ERC721 tokens.
*/
abstract contract NFTBridgeLimits is Ownable {
// token == 0x00..00 represents global restriction applied for all tokens
event TokenBridgingDisabled(address indexed token, bool disabled);
event TokenExecutionDisabled(address indexed token, bool disabled);
/**
* @dev Checks if specified token was already bridged at least once.
* @param _token address of the token contract.
* @return true, if token was already bridged.
*/
function isTokenRegistered(address _token) public view virtual returns (bool);
/**
* @dev Disabled bridging operations for the particular token.
* @param _token address of the token contract, or address(0) for configuring the global restriction.
* @param _disable true for disabling.
*/
function disableTokenBridging(address _token, bool _disable) external onlyOwner {
require(_token == address(0) || isTokenRegistered(_token));
boolStorage[keccak256(abi.encodePacked("bridgingDisabled", _token))] = _disable;
emit TokenBridgingDisabled(_token, _disable);
}
/**
* @dev Disabled execution operations for the particular token.
* @param _token address of the token contract, or address(0) for configuring the global restriction.
* @param _disable true for disabling.
*/
function disableTokenExecution(address _token, bool _disable) external onlyOwner {
require(_token == address(0) || isTokenRegistered(_token));
boolStorage[keccak256(abi.encodePacked("executionDisabled", _token))] = _disable;
emit TokenExecutionDisabled(_token, _disable);
}
/**
* @dev Tells if the bridging operations for the particular token are allowed.
* @param _token address of the token contract.
* @return true, if bridging operations are allowed.
*/
function isTokenBridgingAllowed(address _token) public view returns (bool) {
bool isDisabled = boolStorage[keccak256(abi.encodePacked("bridgingDisabled", _token))];
if (isDisabled || _token == address(0)) {
return !isDisabled;
}
return isTokenBridgingAllowed(address(0));
}
/**
* @dev Tells if the execution operations for the particular token are allowed.
* @param _token address of the token contract.
* @return true, if execution operations are allowed.
*/
function isTokenExecutionAllowed(address _token) public view returns (bool) {
bool isDisabled = boolStorage[keccak256(abi.encodePacked("executionDisabled", _token))];
if (isDisabled || _token == address(0)) {
return !isDisabled;
}
return isTokenExecutionAllowed(address(0));
}
}

FailedMessagesProcessor.sol

pragma solidity 0.7.5;
import "../../../BasicAMBMediator.sol";
import "./BridgeOperationsStorage.sol";
/**
* @title FailedMessagesProcessor
* @dev Functionality for fixing failed bridging operations.
*/
abstract contract FailedMessagesProcessor is BasicAMBMediator, BridgeOperationsStorage {
event FailedMessageFixed(bytes32 indexed messageId, address token);
/**
* @dev Method to be called when a bridged message execution failed. It will generate a new message requesting to
* fix/roll back the transferred assets on the other network.
* It is important to specify parameters very carefully.
* Please, take exact values from the TokensBridgingInitiated event. Otherwise, execution will revert.
* @param _messageId id of the message which execution failed.
* @param _token address of the bridged token on the other side of the bridge.
* @param _sender address of the tokens sender on the other side.
* @param _tokenIds ids of the sent tokens.
* @param _values amounts of tokens sent.
*/
function requestFailedMessageFix(
bytes32 _messageId,
address _token,
address _sender,
uint256[] calldata _tokenIds,
uint256[] calldata _values
) external {
require(_tokenIds.length > 0);
require(_values.length == 0 || _tokenIds.length == _values.length);
IAMB bridge = bridgeContract();
require(!bridge.messageCallStatus(_messageId));
require(bridge.failedMessageReceiver(_messageId) == address(this));
require(bridge.failedMessageSender(_messageId) == mediatorContractOnOtherSide());
bytes memory data =
abi.encodeWithSelector(this.fixFailedMessage.selector, _messageId, _token, _sender, _tokenIds, _values);
_passMessage(data, false);
}
/**
* @dev Handles the request to fix transferred assets which bridged message execution failed on the other network.
* Compares the reconstructed message checksum with the original one. Revert if message params were altered.
* @param _messageId id of the message which execution failed on this side of the bridge.
* @param _token address of the bridged token on this side of the bridge.
* @param _sender address of the tokens sender on this side of the bridge.
* @param _tokenIds ids of the sent tokens.
* @param _values amounts of tokens sent.
*/
function fixFailedMessage(
bytes32 _messageId,
address _token,
address _sender,
uint256[] memory _tokenIds,
uint256[] memory _values
) public onlyMediator {
require(!messageFixed(_messageId));
require(getMessageChecksum(_messageId) == _messageChecksum(_token, _sender, _tokenIds, _values));
setMessageFixed(_messageId);
executeActionOnFixedTokens(_token, _sender, _tokenIds, _values);
emit FailedMessageFixed(_messageId, _token);
}
/**
* @dev Tells if a message sent to the AMB bridge has been fixed.
* @return bool indicating the status of the message.
*/
function messageFixed(bytes32 _messageId) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("messageFixed", _messageId))];
}
/**
* @dev Sets that the message sent to the AMB bridge has been fixed.
* @param _messageId of the message sent to the bridge.
*/
function setMessageFixed(bytes32 _messageId) internal {
boolStorage[keccak256(abi.encodePacked("messageFixed", _messageId))] = true;
}
function executeActionOnFixedTokens(
address _token,
address _recipient,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal virtual;
}

ERC721Relayer.sol

pragma solidity 0.7.5;
import "../../../../interfaces/IBurnableMintableERC721Token.sol";
import "../../../ReentrancyGuard.sol";
import "./BaseRelayer.sol";
/**
* @title ERC721Relayer
* @dev Functionality for bridging multiple ERC721 tokens to the other side of the bridge.
*/
abstract contract ERC721Relayer is BaseRelayer, ReentrancyGuard {
/**
* @dev ERC721 transfer callback function.
* @param _from address of token sender.
* @param _tokenId id of the transferred token.
* @param _data additional transfer data, can be used for passing alternative receiver address.
*/
function onERC721Received(
address,
address _from,
uint256 _tokenId,
bytes calldata _data
) external returns (bytes4) {
if (!lock()) {
bridgeSpecificActionsOnTokenTransfer(
msg.sender,
_from,
_chooseReceiver(_from, _data),
_singletonArray(_tokenId),
new uint256[](0)
);
}
return msg.sig;
}
/**
* @dev Initiate the bridge operation for some token from msg.sender.
* The user should first call Approve method of the ERC721 token.
* @param token bridged token contract address.
* @param _receiver address that will receive the token on the other network.
* @param _tokenId id of the token to be transferred to the other network.
*/
function relayToken(
IERC721 token,
address _receiver,
uint256 _tokenId
) external {
_relayToken(token, _receiver, _tokenId);
}
/**
* @dev Initiate the bridge operation for some token from msg.sender to msg.sender on the other side.
* The user should first call Approve method of the ERC721 token.
* @param token bridged token contract address.
* @param _tokenId id of token to be transferred to the other network.
*/
function relayToken(IERC721 token, uint256 _tokenId) external {
_relayToken(token, msg.sender, _tokenId);
}
/**
* @dev Validates that the token amount is inside the limits, calls transferFrom to transfer the token to the contract
* and invokes the method to burn/lock the token and unlock/mint the token on the other network.
* The user should first call Approve method of the ERC721 token.
* @param _token bridge token contract address.
* @param _receiver address that will receive the token on the other network.
* @param _tokenId id of the token to be transferred to the other network.
*/
function _relayToken(
IERC721 _token,
address _receiver,
uint256 _tokenId
) internal {
// This lock is to prevent calling bridgeSpecificActionsOnTokenTransfer twice.
// When transferFrom is called, after the transfer, the ERC721 token might call onERC721Received from this contract
// which will call bridgeSpecificActionsOnTokenTransfer.
require(!lock());
setLock(true);
_token.transferFrom(msg.sender, address(this), _tokenId);
setLock(false);
bridgeSpecificActionsOnTokenTransfer(
address(_token),
msg.sender,
_receiver,
_singletonArray(_tokenId),
new uint256[](0)
);
}
}

BridgeOperationsStorage.sol

pragma solidity 0.7.5;
import "../../../../upgradeability/EternalStorage.sol";
/**
* @title BridgeOperationsStorage
* @dev Functionality for storing processed bridged operations.
*/
abstract contract BridgeOperationsStorage is EternalStorage {
/**
* @dev Set bridged message checksum.
* @param _messageId id of the sent AMB message.
* @param _checksum checksum of the bridge operation.
*/
function setMessageChecksum(bytes32 _messageId, bytes32 _checksum) internal {
uintStorage[keccak256(abi.encodePacked("messageChecksum", _messageId))] = uint256(_checksum);
}
/**
* @dev Tells the bridged message checksum.
* @param _messageId id of the sent AMB message.
* @return saved message checksum associated with the given message id.
*/
function getMessageChecksum(bytes32 _messageId) internal view returns (bytes32) {
return bytes32(uintStorage[keccak256(abi.encodePacked("messageChecksum", _messageId))]);
}
/**
* @dev Calculates message checksum, used for verifying correctness of the given parameters when fixing message.
* @param _token address of the bridged token contract on this side current side of the bridge.
* @param _sender address of the tokens sender.
* @param _tokenIds list of ids of sent tokens.
* @param _values list of sent token amounts. Should be an empty array for ERC721 tokens.
* @return message checksum.
*/
function _messageChecksum(
address _token,
address _sender,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal pure returns (bytes32) {
return keccak256(abi.encode(_token, _sender, _tokenIds, _values));
}
}

BaseRelayer.sol

pragma solidity 0.7.5;
import "../../../../libraries/Bytes.sol";
/**
* @title BaseRelayer
* @dev Basic functionality for relaying different NFT tokens to the other side of the bridge.
*/
abstract contract BaseRelayer {
/**
* @dev Helper function for alternative receiver feature. Chooses the actual receiver out of sender and passed data.
* @param _from address of the token sender.
* @param _data passed data in the transfer message.
* @return recipient address of the receiver on the other side.
*/
function _chooseReceiver(address _from, bytes memory _data) internal pure returns (address recipient) {
recipient = _from;
if (_data.length > 0) {
require(_data.length == 20);
recipient = Bytes.bytesToAddress(_data);
}
}
/**
* @dev Wraps a given uint256 value into an array with a single element.
* @param _value argument to wrap.
* @return wrapper array.
*/
function _singletonArray(uint256 _value) internal pure returns (uint256[] memory) {
uint256[] memory array = new uint256[](1);
array[0] = _value;
return array;
}
function bridgeSpecificActionsOnTokenTransfer(
address _token,
address _from,
address _receiver,
uint256[] memory _tokenIds,
uint256[] memory _values
) internal virtual;
}

TokenImageStorage.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/utils/Address.sol";
import "../../../Ownable.sol";
/**
* @title TokenImageStorage
* @dev Storage functionality for working with ERC721/ERC1155 image contract.
*/
contract TokenImageStorage is Ownable {
bytes32 internal constant ERC721_TOKEN_IMAGE_CONTRACT =
0x20b8ca26cc94f39fab299954184cf3a9bd04f69543e4f454fab299f015b8130f; // keccak256(abi.encodePacked("tokenImageContract"))
bytes32 internal constant ERC1155_TOKEN_IMAGE_CONTRACT =
0x02e1d283efd236e8b97cefe072f0c863fa2db9f9ba42b0eca53ab31c49067a67; // keccak256(abi.encodePacked("erc1155tokenImageContract"))
/**
* @dev Updates address of the used ERC721 token image.
* Only owner can call this method.
* @param _image address of the new token image.
*/
function setTokenImageERC721(address _image) external onlyOwner {
_setTokenImageERC721(_image);
}
/**
* @dev Updates address of the used ERC1155 token image.
* Only owner can call this method.
* @param _image address of the new token image.
*/
function setTokenImageERC1155(address _image) external onlyOwner {
_setTokenImageERC1155(_image);
}
/**
* @dev Tells the address of the used ERC721 token image.
* @return address of the used token image.
*/
function tokenImageERC721() public view returns (address) {
return addressStorage[ERC721_TOKEN_IMAGE_CONTRACT];
}
/**
* @dev Tells the address of the used ERC1155 token image.
* @return address of the used token image.
*/
function tokenImageERC1155() public view returns (address) {
return addressStorage[ERC1155_TOKEN_IMAGE_CONTRACT];
}
/**
* @dev Internal function for updating address of the used ERC721 token image.
* @param _image address of the new token image.
*/
function _setTokenImageERC721(address _image) internal {
require(Address.isContract(_image));
addressStorage[ERC721_TOKEN_IMAGE_CONTRACT] = _image;
}
/**
* @dev Internal function for updating address of the used ERC1155 token image.
* @param _image address of the new token image.
*/
function _setTokenImageERC1155(address _image) internal {
require(Address.isContract(_image));
addressStorage[ERC1155_TOKEN_IMAGE_CONTRACT] = _image;
}
}

ERC721TokenProxy.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/utils/Address.sol";
import "../../../../upgradeability/Proxy.sol";
import "../../../../interfaces/IOwnable.sol";
/**
* @title ERC721TokenProxy
* @dev Helps to reduces the size of the deployed bytecode for automatically created tokens, by using a proxy contract.
*/
contract ERC721TokenProxy is Proxy {
// storage layout is copied from ERC721BridgeToken.sol
mapping(bytes4 => bool) private _supportedInterfaces;
mapping(address => uint256) private _holderTokens;
//EnumerableMap.UintToAddressMap private _tokenOwners;
uint256[] private _tokenOwnersEntries;
mapping(bytes32 => uint256) private _tokenOwnersIndexes;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
string private name;
string private symbol;
mapping(uint256 => string) private _tokenURIs;
string private _baseURI;
address private bridgeContract;
/**
* @dev Creates an upgradeable token proxy for ERC721BridgeToken.sol, initializes its eternalStorage.
* @param _tokenImage address of the token image used for mirroring all functions.
* @param _name token name.
* @param _symbol token symbol.
* @param _owner address of the owner for this contract.
*/
constructor(
address _tokenImage,
string memory _name,
string memory _symbol,
address _owner
) {
assembly {
// EIP 1967
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, _tokenImage)
}
name = _name;
symbol = _symbol;
bridgeContract = _owner; // _owner == HomeOmnibridgeNFT/ForeignOmnibridgeNFT mediator
}
/**
* @dev Retrieves the implementation contract address, mirrored token image.
* @return impl token image address.
*/
function implementation() public view override returns (address impl) {
assembly {
impl := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
}
}
/**
* @dev Updates the implementation contract address.
* Only the bridge and bridge owner can call this method.
* @param _implementation address of the new implementation.
*/
function setImplementation(address _implementation) external {
require(msg.sender == bridgeContract || msg.sender == IOwnable(bridgeContract).owner());
require(_implementation != address(0));
require(Address.isContract(_implementation));
assembly {
// EIP 1967
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, _implementation)
}
}
/**
* @dev Tells the current version of the ERC721 token proxy interfaces.
*/
function getTokenProxyInterfacesVersion()
external
pure
returns (
uint64 major,
uint64 minor,
uint64 patch
)
{
return (1, 0, 0);
}
}

ERC1155TokenProxy.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/utils/Address.sol";
import "../../../../upgradeability/Proxy.sol";
import "../../../../interfaces/IOwnable.sol";
/**
* @title ERC1155TokenProxy
* @dev Helps to reduces the size of the deployed bytecode for automatically created tokens, by using a proxy contract.
*/
contract ERC1155TokenProxy is Proxy {
// storage layout is copied from ERC1155BridgeToken.sol
mapping(bytes4 => bool) private _supportedInterfaces;
// Mapping from token ID to account balances
mapping(uint256 => mapping(address => uint256)) private _balances;
// Mapping from account to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
// not used, kept for correct storage layout representation
string private _uri;
string private name;
string private symbol;
mapping(uint256 => string) private _tokenURIs;
string private _baseURI;
address private bridgeContract;
/**
* @dev Creates an upgradeable token proxy for ERC1155BridgeToken.sol, initializes its eternalStorage.
* @param _tokenImage address of the token image used for mirroring all functions.
* @param _name token name.
* @param _symbol token symbol.
* @param _owner address of the owner for this contract.
*/
constructor(
address _tokenImage,
string memory _name,
string memory _symbol,
address _owner
) {
assembly {
// EIP 1967
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, _tokenImage)
}
name = _name;
symbol = _symbol;
bridgeContract = _owner; // _owner == HomeOmnibridgeNFT/ForeignOmnibridgeNFT mediator
}
/**
* @dev Retrieves the implementation contract address, mirrored token image.
* @return impl token image address.
*/
function implementation() public view override returns (address impl) {
assembly {
impl := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
}
}
/**
* @dev Updates the implementation contract address.
* Only the bridge and bridge owner can call this method.
* @param _implementation address of the new implementation.
*/
function setImplementation(address _implementation) external {
require(msg.sender == bridgeContract || msg.sender == IOwnable(bridgeContract).owner());
require(_implementation != address(0));
require(Address.isContract(_implementation));
assembly {
// EIP 1967
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, _implementation)
}
}
/**
* @dev Tells the current version of the ERC1155 token proxy interfaces.
*/
function getTokenProxyInterfacesVersion()
external
pure
returns (
uint64 major,
uint64 minor,
uint64 patch
)
{
return (1, 0, 0);
}
}

BridgedTokensRegistry.sol

pragma solidity 0.7.5;
import "../../../../upgradeability/EternalStorage.sol";
/**
* @title BridgedTokensRegistry
* @dev Functionality for keeping track of registered bridged token pairs.
*/
contract BridgedTokensRegistry is EternalStorage {
event NewTokenRegistered(address indexed nativeToken, address indexed bridgedToken);
/**
* @dev Retrieves address of the bridged token contract associated with a specific native token contract on the other side.
* @param _nativeToken address of the native token contract on the other side.
* @return address of the deployed bridged token contract.
*/
function bridgedTokenAddress(address _nativeToken) public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("homeTokenAddress", _nativeToken))];
}
/**
* @dev Retrieves address of the native token contract associated with a specific bridged token contract.
* @param _bridgedToken address of the created bridged token contract on this side.
* @return address of the native token contract on the other side of the bridge.
*/
function nativeTokenAddress(address _bridgedToken) public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("foreignTokenAddress", _bridgedToken))];
}
/**
* @dev Internal function for updating a pair of addresses for the bridged token.
* @param _nativeToken address of the native token contract on the other side.
* @param _bridgedToken address of the created bridged token contract on this side.
*/
function _setTokenAddressPair(address _nativeToken, address _bridgedToken) internal {
addressStorage[keccak256(abi.encodePacked("homeTokenAddress", _nativeToken))] = _bridgedToken;
addressStorage[keccak256(abi.encodePacked("foreignTokenAddress", _bridgedToken))] = _nativeToken;
emit NewTokenRegistered(_nativeToken, _bridgedToken);
}
}

ReentrancyGuard.sol

pragma solidity 0.7.5;
contract ReentrancyGuard {
function lock() internal view returns (bool res) {
assembly {
// Even though this is not the same as boolStorage[keccak256(abi.encodePacked("lock"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
res := sload(0x6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e92) // keccak256(abi.encodePacked("lock"))
}
}
function setLock(bool _lock) internal {
assembly {
// Even though this is not the same as boolStorage[keccak256(abi.encodePacked("lock"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
sstore(0x6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e92, _lock) // keccak256(abi.encodePacked("lock"))
}
}
}

Ownable.sol

pragma solidity 0.7.5;
import "../upgradeability/EternalStorage.sol";
import "../interfaces/IUpgradeabilityOwnerStorage.sol";
/**
* @title Ownable
* @dev This contract has an owner address providing basic authorization control
*/
contract Ownable is EternalStorage {
bytes4 internal constant UPGRADEABILITY_OWNER = 0x6fde8202; // upgradeabilityOwner()
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner());
_;
}
/**
* @dev Throws if called through proxy by any account other than contract itself or an upgradeability owner.
*/
modifier onlyRelevantSender() {
(bool isProxy, bytes memory returnData) =
address(this).staticcall(abi.encodeWithSelector(UPGRADEABILITY_OWNER));
require(
!isProxy || // covers usage without calling through storage proxy
(returnData.length == 32 && msg.sender == abi.decode(returnData, (address))) || // covers usage through regular proxy calls
msg.sender == address(this) // covers calls through upgradeAndCall proxy method
);
_;
}
bytes32 internal constant OWNER = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256(abi.encodePacked("owner"))
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() public view returns (address) {
return addressStorage[OWNER];
}
/**
* @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) external onlyOwner {
_setOwner(newOwner);
}
/**
* @dev Sets a new owner address
*/
function _setOwner(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(owner(), newOwner);
addressStorage[OWNER] = newOwner;
}
}

Initializable.sol

pragma solidity 0.7.5;
import "../upgradeability/EternalStorage.sol";
contract Initializable is EternalStorage {
bytes32 internal constant INITIALIZED = 0x0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba; // keccak256(abi.encodePacked("isInitialized"))
function setInitialize() internal {
boolStorage[INITIALIZED] = true;
}
function isInitialized() public view returns (bool) {
return boolStorage[INITIALIZED];
}
}

BasicAMBMediator.sol

pragma solidity 0.7.5;
import "./Ownable.sol";
import "../interfaces/IAMB.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/**
* @title BasicAMBMediator
* @dev Basic storage and methods needed by mediators to interact with AMB bridge.
*/
abstract contract BasicAMBMediator is Ownable {
bytes32 internal constant BRIDGE_CONTRACT = 0x811bbb11e8899da471f0e69a3ed55090fc90215227fc5fb1cb0d6e962ea7b74f; // keccak256(abi.encodePacked("bridgeContract"))
bytes32 internal constant MEDIATOR_CONTRACT = 0x98aa806e31e94a687a31c65769cb99670064dd7f5a87526da075c5fb4eab9880; // keccak256(abi.encodePacked("mediatorContract"))
/**
* @dev Throws if caller on the other side is not an associated mediator.
*/
modifier onlyMediator {
_onlyMediator();
_;
}
/**
* @dev Internal function for reducing onlyMediator modifier bytecode overhead.
*/
function _onlyMediator() internal view {
IAMB bridge = bridgeContract();
require(msg.sender == address(bridge));
require(bridge.messageSender() == mediatorContractOnOtherSide());
}
/**
* @dev Sets the AMB bridge contract address. Only the owner can call this method.
* @param _bridgeContract the address of the bridge contract.
*/
function setBridgeContract(address _bridgeContract) external onlyOwner {
_setBridgeContract(_bridgeContract);
}
/**
* @dev Sets the mediator contract address from the other network. Only the owner can call this method.
* @param _mediatorContract the address of the mediator contract.
*/
function setMediatorContractOnOtherSide(address _mediatorContract) external onlyOwner {
_setMediatorContractOnOtherSide(_mediatorContract);
}
/**
* @dev Get the AMB interface for the bridge contract address
* @return AMB interface for the bridge contract address
*/
function bridgeContract() public view returns (IAMB) {
return IAMB(addressStorage[BRIDGE_CONTRACT]);
}
/**
* @dev Tells the mediator contract address from the other network.
* @return the address of the mediator contract.
*/
function mediatorContractOnOtherSide() public view virtual returns (address) {
return addressStorage[MEDIATOR_CONTRACT];
}
/**
* @dev Stores a valid AMB bridge contract address.
* @param _bridgeContract the address of the bridge contract.
*/
function _setBridgeContract(address _bridgeContract) internal {
require(Address.isContract(_bridgeContract));
addressStorage[BRIDGE_CONTRACT] = _bridgeContract;
}
/**
* @dev Stores the mediator contract address from the other network.
* @param _mediatorContract the address of the mediator contract.
*/
function _setMediatorContractOnOtherSide(address _mediatorContract) internal {
addressStorage[MEDIATOR_CONTRACT] = _mediatorContract;
}
/**
* @dev Tells the id of the message originated on the other network.
* @return the id of the message originated on the other network.
*/
function messageId() internal view returns (bytes32) {
return bridgeContract().messageId();
}
/**
* @dev Tells the maximum gas limit that a message can use on its execution by the AMB bridge on the other network.
* @return the maximum gas limit value.
*/
function maxGasPerTx() internal view returns (uint256) {
return bridgeContract().maxGasPerTx();
}
function _passMessage(bytes memory _data, bool _useOracleLane) internal virtual returns (bytes32);
}

Proxy.sol

pragma solidity 0.7.5;
/**
* @title Proxy
* @dev Gives the possibility to delegate any call to a foreign implementation.
*/
abstract contract Proxy {
/**
* @dev Tells the address of the implementation where every call will be delegated.
* @return address of the implementation to which it will be delegated
*/
function implementation() public view virtual returns (address);
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
fallback() external payable {
// solhint-disable-previous-line no-complex-fallback
address _impl = implementation();
require(_impl != address(0));
assembly {
/*
0x40 is the "free memory slot", meaning a pointer to next slot of empty memory. mload(0x40)
loads the data in the free memory slot, so `ptr` is a pointer to the next slot of empty
memory. It's needed because we're going to write the return data of delegatecall to the
free memory slot.
*/
let ptr := mload(0x40)
/*
`calldatacopy` is copy calldatasize bytes from calldata
First argument is the destination to which data is copied(ptr)
Second argument specifies the start position of the copied data.
Since calldata is sort of its own unique location in memory,
0 doesn't refer to 0 in memory or 0 in storage - it just refers to the zeroth byte of calldata.
That's always going to be the zeroth byte of the function selector.
Third argument, calldatasize, specifies how much data will be copied.
calldata is naturally calldatasize bytes long (same thing as msg.data.length)
*/
calldatacopy(ptr, 0, calldatasize())
/*
delegatecall params explained:
gas: the amount of gas to provide for the call. `gas` is an Opcode that gives
us the amount of gas still available to execution
_impl: address of the contract to delegate to
ptr: to pass copied data
calldatasize: loads the size of `bytes memory data`, same as msg.data.length
0, 0: These are for the `out` and `outsize` params. Because the output could be dynamic,
these are set to 0, 0 so the output data will not be written to memory. The output
data will be read using `returndatasize` and `returdatacopy` instead.
result: This will be 0 if the call fails and 1 if it succeeds
*/
let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
/*
*/
/*
ptr current points to the value stored at 0x40,
because we assigned it like ptr := mload(0x40).
Because we use 0x40 as a free memory pointer,
we want to make sure that the next time we want to allocate memory,
we aren't overwriting anything important.
So, by adding ptr and returndatasize,
we get a memory location beyond the end of the data we will be copying to ptr.
We place this in at 0x40, and any reads from 0x40 will now read from free memory
*/
mstore(0x40, add(ptr, returndatasize()))
/*
`returndatacopy` is an Opcode that copies the last return data to a slot. `ptr` is the
slot it will copy to, 0 means copy from the beginning of the return data, and size is
the amount of data to copy.
`returndatasize` is an Opcode that gives us the size of the last return data. In this case, that is the size of the data returned from delegatecall
*/
returndatacopy(ptr, 0, returndatasize())
/*
if `result` is 0, revert.
if `result` is 1, return `size` amount of data from `ptr`. This is the data that was
copied to `ptr` from the delegatecall return data
*/
switch result
case 0 {
revert(ptr, returndatasize())
}
default {
return(ptr, returndatasize())
}
}
}
}

EternalStorage.sol

pragma solidity 0.7.5;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
}

ERC721BridgeToken.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "../interfaces/IOwnable.sol";
import "../interfaces/IBurnableMintableERC721Token.sol";
/**
* @title ERC721BridgeToken
* @dev template token contract for bridged ERC721 tokens.
*/
contract ERC721BridgeToken is ERC721, IBurnableMintableERC721Token {
address public bridgeContract;
constructor(
string memory _name,
string memory _symbol,
address _bridgeContract
) ERC721(_name, _symbol) {
bridgeContract = _bridgeContract;
}
/**
* @dev Throws if sender is not a bridge contract.
*/
modifier onlyBridge() {
require(msg.sender == bridgeContract);
_;
}
/**
* @dev Throws if sender is not a bridge contract or bridge contract owner.
*/
modifier onlyOwner() {
require(msg.sender == bridgeContract || msg.sender == IOwnable(bridgeContract).owner());
_;
}
/**
* @dev Tells if this contract implements the interface defined by
* `interfaceId`. See the corresponding EIP165.
* @return true, if interface is implemented.
*/
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
bytes4 INTERFACE_ID_ERC165 = 0x01ffc9a7;
bytes4 INTERFACE_ID_ERC721 = 0x80ac58cd;
bytes4 INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
bytes4 INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
return
interfaceId == INTERFACE_ID_ERC165 ||
interfaceId == INTERFACE_ID_ERC721 ||
interfaceId == INTERFACE_ID_ERC721_METADATA ||
interfaceId == INTERFACE_ID_ERC721_ENUMERABLE;
}
/**
* @dev Stub for preventing unneeded storage writes.
* All supported interfaces are hardcoded in the supportsInterface function.
*/
function _registerInterface(bytes4) internal override {}
/**
* @dev Mint new ERC721 token.
* Only bridge contract is authorized to mint tokens.
* @param _to address of the newly created token owner.
* @param _tokenId unique identifier of the minted token.
*/
function mint(address _to, uint256 _tokenId) external override onlyBridge {
_safeMint(_to, _tokenId);
}
/**
* @dev Burns some ERC721 token.
* Only bridge contract is authorized to burn tokens.
* @param _tokenId unique identifier of the burned token.
*/
function burn(uint256 _tokenId) external override onlyBridge {
_burn(_tokenId);
}
// hack to access private fields in ERC721 contract
struct MetadataStorage {
string name;
string symbol;
}
/**
* @dev Updated bridged token name/symbol parameters.
* Only bridge owner or bridge itself can call this method.
* @param _name new name parameter, will be saved as is, without additional suffixes like " from Mainnet".
* @param _symbol new symbol parameter.
*/
function setMetadata(string calldata _name, string calldata _symbol) external onlyOwner {
require(bytes(_name).length > 0 && bytes(_symbol).length > 0);
MetadataStorage storage metadata;
assembly {
metadata.slot := 6
}
metadata.name = _name;
metadata.symbol = _symbol;
}
/**
* @dev Sets the base URI for all tokens.
* Can be called by bridge owner after token contract was instantiated.
* @param _baseURI new base URI.
*/
function setBaseURI(string calldata _baseURI) external onlyOwner {
_setBaseURI(_baseURI);
}
/**
* @dev Updates the bridge contract address.
* Can be called by bridge owner after token contract was instantiated.
* @param _bridgeContract address of the new bridge contract.
*/
function setBridgeContract(address _bridgeContract) external onlyOwner {
require(_bridgeContract != address(0));
bridgeContract = _bridgeContract;
}
/**
* @dev Sets the URI for the particular token.
* Can be called by bridge owner after token bridging.
* @param _tokenId URI for the bridged token metadata.
* @param _tokenURI new token URI.
*/
function setTokenURI(uint256 _tokenId, string calldata _tokenURI) external override onlyOwner {
_setTokenURI(_tokenId, _tokenURI);
}
/**
* @dev Tells the current version of the ERC721 token interfaces.
*/
function getTokenInterfacesVersion()
external
pure
returns (
uint64 major,
uint64 minor,
uint64 patch
)
{
return (1, 1, 0);
}
}

Bytes.sol

pragma solidity 0.7.5;
/**
* @title Bytes
* @dev Helper methods to transform bytes to other solidity types.
*/
library Bytes {
/**
* @dev Truncate bytes array if its size is more than 20 bytes.
* NOTE: This function does not perform any checks on the received parameter.
* Make sure that the _bytes argument has a correct length, not less than 20 bytes.
* A case when _bytes has length less than 20 will lead to the undefined behaviour,
* since assembly will read data from memory that is not related to the _bytes argument.
* @param _bytes to be converted to address type
* @return addr address included in the firsts 20 bytes of the bytes array in parameter.
*/
function bytesToAddress(bytes memory _bytes) internal pure returns (address addr) {
assembly {
addr := mload(add(_bytes, 20))
}
}
}

IERC1155TokenReceiver.sol

pragma solidity 0.7.5;
/**
Note: The ERC-165 identifier for this interface is 0x4e2312e0.
*/
interface IERC1155TokenReceiver {
/**
@notice Handle the receipt of a single ERC1155 token type.
@dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer.
This function MUST revert if it rejects the transfer.
Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
@param _operator The address which initiated the transfer (i.e. msg.sender)
@param _from The address which previously owned the token
@param _id The ID of the token being transferred
@param _value The amount of tokens being transferred
@param _data Additional data with no specified format
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
*/
function onERC1155Received(
address _operator,
address _from,
uint256 _id,
uint256 _value,
bytes calldata _data
) external returns (bytes4);
/**
@notice Handle the receipt of multiple ERC1155 token types.
@dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s).
This function MUST revert if it rejects the transfer(s).
Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
@param _operator The address which initiated the batch transfer (i.e. msg.sender)
@param _from The address which previously owned the token
@param _ids An array containing ids of each token being transferred (order and length must match _values array)
@param _values An array containing amounts of each token being transferred (order and length must match _ids array)
@param _data Additional data with no specified format
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
*/
function onERC1155BatchReceived(
address _operator,
address _from,
uint256[] calldata _ids,
uint256[] calldata _values,
bytes calldata _data
) external returns (bytes4);
}

IBurnableMintableERC721Token.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
interface IBurnableMintableERC721Token is IERC721 {
function mint(address _to, uint256 _tokeId) external;
function burn(uint256 _tokenId) external;
function setTokenURI(uint256 _tokenId, string calldata _tokenURI) external;
}

IBurnableMintableERC1155Token.sol

pragma solidity 0.7.5;
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
interface IBurnableMintableERC1155Token is IERC1155 {
function mint(
address _to,
uint256[] calldata _tokenIds,
uint256[] calldata _values
) external;
function burn(uint256[] calldata _tokenId, uint256[] calldata _values) external;
}

IAMB.sol

pragma solidity 0.7.5;
interface IAMB {
event UserRequestForAffirmation(bytes32 indexed messageId, bytes encodedData);
event UserRequestForSignature(bytes32 indexed messageId, bytes encodedData);
event CollectedSignatures(
address authorityResponsibleForRelay,
bytes32 messageHash,
uint256 numberOfCollectedSignatures
);
event AffirmationCompleted(
address indexed sender,
address indexed executor,
bytes32 indexed messageId,
bool status
);
event RelayedMessage(address indexed sender, address indexed executor, bytes32 indexed messageId, bool status);
function messageSender() external view returns (address);
function maxGasPerTx() external view returns (uint256);
function transactionHash() external view returns (bytes32);
function messageId() external view returns (bytes32);
function messageSourceChainId() external view returns (bytes32);
function messageCallStatus(bytes32 _messageId) external view returns (bool);
function failedMessageDataHash(bytes32 _messageId) external view returns (bytes32);
function failedMessageReceiver(bytes32 _messageId) external view returns (address);
function failedMessageSender(bytes32 _messageId) external view returns (address);
function requireToPassMessage(
address _contract,
bytes calldata _data,
uint256 _gas
) external returns (bytes32);
function requireToConfirmMessage(
address _contract,
bytes calldata _data,
uint256 _gas
) external returns (bytes32);
function sourceChainId() external view returns (uint256);
function destinationChainId() external view returns (uint256);
}

Strings.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev String operations.
*/
library Strings {
/**
* @dev Converts a `uint256` to its ASCII `string` representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = bytes1(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}

EnumerableSet.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.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.3.0, sets of type `bytes32` (`Bytes32Set`), `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];
}
// Bytes32Set
struct Bytes32Set {
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(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, 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(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set 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(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, 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(uint160(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(uint160(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(uint160(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(uint160(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));
}
}

EnumerableMap.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
* supported.
*/
library EnumerableMap {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct MapEntry {
bytes32 _key;
bytes32 _value;
}
struct Map {
// Storage of map keys and values
MapEntry[] _entries;
// Position of the entry defined by a key in the `entries` array, plus 1
// because index 0 means a key is not in the map.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) { // Equivalent to !contains(map, key)
map._entries.push(MapEntry({ _key: key, _value: value }));
// The entry is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
map._indexes[key] = map._entries.length;
return true;
} else {
map._entries[keyIndex - 1]._value = value;
return false;
}
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function _remove(Map storage map, bytes32 key) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex != 0) { // Equivalent to contains(map, key)
// To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
// in the array, and then remove the last entry (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = keyIndex - 1;
uint256 lastIndex = map._entries.length - 1;
// When the entry 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.
MapEntry storage lastEntry = map._entries[lastIndex];
// Move the last entry to the index where the entry to delete is
map._entries[toDeleteIndex] = lastEntry;
// Update the index for the moved entry
map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved entry was stored
map._entries.pop();
// Delete the index for the deleted slot
delete map._indexes[key];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._indexes[key] != 0;
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._entries.length;
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
require(map._entries.length > index, "EnumerableMap: index out of bounds");
MapEntry storage entry = map._entries[index];
return (entry._key, entry._value);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
}
/**
* @dev Same as {_get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {_tryGet}.
*/
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
}
// UintToAddressMap
struct UintToAddressMap {
Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return _remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
/**
* @dev Returns the element 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(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*
* _Available since v3.4._
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
}
}

Context.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.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;
}
}

Address.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @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) {
// 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;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @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");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
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);
}
}
}
}

IERC721Receiver.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

IERC721Metadata.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}

IERC721Enumerable.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}

IERC721.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../../introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

ERC721.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../../utils/Context.sol";
import "./IERC721.sol";
import "./IERC721Metadata.sol";
import "./IERC721Enumerable.sol";
import "./IERC721Receiver.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../utils/EnumerableSet.sol";
import "../../utils/EnumerableMap.sol";
import "../../utils/Strings.sol";
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
using SafeMath for uint256;
using Address for address;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableMap for EnumerableMap.UintToAddressMap;
using Strings for uint256;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSet.UintSet) private _holderTokens;
// Enumerable mapping from token ids to their owners
EnumerableMap.UintToAddressMap private _tokenOwners;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURI;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor (string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _holderTokens[owner].length();
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a prefix in {tokenURI} to each token's URI, or
* to the token ID if no specific URI is set for that token ID.
*/
function baseURI() public view virtual returns (string memory) {
return _baseURI;
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
return _holderTokens[owner].at(index);
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
return _tokenOwners.length();
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `_data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _tokenOwners.contains(tokenId);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
d*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
_mint(to, tokenId);
require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId); // internal owner
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(tokenId);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(from, to, tokenId);
}
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI},
* or to the token ID if {tokenURI} is empty.
*/
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes memory returndata = to.functionCall(abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
), "ERC721: transfer to non ERC721Receiver implementer");
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}

IERC1155MetadataURI.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*
* _Available since v3.1._
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}

IERC1155.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../../introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

SafeMath.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.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, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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 (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @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) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @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) {
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, reverting 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) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting 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) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* 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);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* 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);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* 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;
}
}

IERC165.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

ERC165.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
abstract contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"string","name":"_suffix","internalType":"string"}]},{"type":"event","name":"FailedMessageFixed","inputs":[{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":true},{"type":"address","name":"token","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"NewTokenRegistered","inputs":[{"type":"address","name":"nativeToken","internalType":"address","indexed":true},{"type":"address","name":"bridgedToken","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":false},{"type":"address","name":"newOwner","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"TokenBridgingDisabled","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"bool","name":"disabled","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"TokenExecutionDisabled","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"bool","name":"disabled","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"TokensBridged","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256[]","name":"tokenIds","internalType":"uint256[]","indexed":false},{"type":"uint256[]","name":"values","internalType":"uint256[]","indexed":false},{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"TokensBridgingInitiated","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256[]","name":"tokenIds","internalType":"uint256[]","indexed":false},{"type":"uint256[]","name":"values","internalType":"uint256[]","indexed":false},{"type":"bytes32","name":"messageId","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAMB"}],"name":"bridgeContract","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"bridgedTokenAddress","inputs":[{"type":"address","name":"_nativeToken","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deployAndHandleBridgedNFT","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"string","name":"_name","internalType":"string"},{"type":"string","name":"_symbol","internalType":"string"},{"type":"address","name":"_recipient","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"},{"type":"string[]","name":"_tokenURIs","internalType":"string[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"disableTokenBridging","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"bool","name":"_disable","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"disableTokenExecution","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"bool","name":"_disable","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fixFailedMessage","inputs":[{"type":"bytes32","name":"_messageId","internalType":"bytes32"},{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_sender","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fixMediatorBalanceERC1155","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_receiver","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fixMediatorBalanceERC721","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_receiver","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract NFTForwardingRulesManager"}],"name":"forwardingRulesManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract SelectorTokenGasLimitManager"}],"name":"gasLimitManager","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint64","name":"major","internalType":"uint64"},{"type":"uint64","name":"minor","internalType":"uint64"},{"type":"uint64","name":"patch","internalType":"uint64"}],"name":"getBridgeInterfacesVersion","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes4","name":"_data","internalType":"bytes4"}],"name":"getBridgeMode","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"handleBridgedNFT","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_recipient","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"},{"type":"string[]","name":"_tokenURIs","internalType":"string[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"handleNativeNFT","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_recipient","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"initialize","inputs":[{"type":"address","name":"_bridgeContract","internalType":"address"},{"type":"address","name":"_mediatorContract","internalType":"address"},{"type":"address","name":"_gasLimitManager","internalType":"address"},{"type":"address","name":"_owner","internalType":"address"},{"type":"address","name":"_imageERC721","internalType":"address"},{"type":"address","name":"_imageERC1155","internalType":"address"},{"type":"address","name":"_forwardingRulesManager","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isBridgedTokenDeployAcknowledged","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isInitialized","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isRegisteredAsNativeToken","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTokenBridgingAllowed","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTokenExecutionAllowed","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTokenRegistered","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"mediatorContractOnOtherSide","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"}],"name":"mediatorOwns","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"messageFixed","inputs":[{"type":"bytes32","name":"_messageId","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"migrationTo3_0_0","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"nativeTokenAddress","inputs":[{"type":"address","name":"_bridgedToken","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC1155BatchReceived","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"_from","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC1155Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"uint256","name":"_value","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"relayToken","inputs":[{"type":"address","name":"token","internalType":"contract IERC721"},{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"relayToken","inputs":[{"type":"address","name":"token","internalType":"contract IERC721"},{"type":"address","name":"_receiver","internalType":"address"},{"type":"uint256","name":"_tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"requestFailedMessageFix","inputs":[{"type":"bytes32","name":"_messageId","internalType":"bytes32"},{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_sender","internalType":"address"},{"type":"uint256[]","name":"_tokenIds","internalType":"uint256[]"},{"type":"uint256[]","name":"_values","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBridgeContract","inputs":[{"type":"address","name":"_bridgeContract","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCustomMetadata","inputs":[{"type":"address","name":"_token","internalType":"address"},{"type":"string","name":"_name","internalType":"string"},{"type":"string","name":"_symbol","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCustomTokenAddressPair","inputs":[{"type":"address","name":"_nativeToken","internalType":"address"},{"type":"address","name":"_bridgedToken","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setForwardingRulesManager","inputs":[{"type":"address","name":"_manager","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setGasLimitManager","inputs":[{"type":"address","name":"_manager","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMediatorContractOnOtherSide","inputs":[{"type":"address","name":"_mediatorContract","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTokenImageERC1155","inputs":[{"type":"address","name":"_image","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTokenImageERC721","inputs":[{"type":"address","name":"_image","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tokenImageERC1155","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tokenImageERC721","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
            

Deployed ByteCode

0x60806040523480156200001157600080fd5b5060043610620002b05760003560e01c806390ad84a81162000171578063c722b1be11620000d5578063e853177b1162000093578063e853177b14620005e8578063f23a6e6114620005ff578063f2fde38b1462000616578063f4ec76d9146200062d578063f92d74681462000644578063fb0a6f30146200065b57620002b0565b8063c722b1be1462000580578063cba002b9146200058a578063cd59658314620005b0578063d79ca76e14620005ba578063e1e5e80314620005d157620002b0565b8063a5bd2942116200012f578063a5bd294214620004f6578063ae813e9f146200050d578063b701e0941462000524578063b7dbbf87146200053b578063bc197c811462000552578063c2173d43146200056957620002b0565b806390ad84a814620004a657806399a935fc14620004bd5780639cb7595a14620004c75780639d4051ae14620004e25780639dd9cde014620004ec57620002b0565b80634ce5cff411620002195780636cb4497211620001d75780636cb4497214620004435780636e5d6bea146200045a5780637996ad3114620004715780637f083f691462000488578063871c076014620004925780638da5cb5b146200049c57620002b0565b80634ce5cff414620003d05780635933998214620003e7578063613fa2f214620003fe57806361c04f8414620004155780636ca48357146200042c57620002b0565b806328f43667116200027357806328f4366714620003515780632d70061f146200036857806335876476146200038e578063392e53cd14620003a5578063437764df14620003af5780634b92d87414620003b957620002b0565b80630b26cf6614620002b55780630b71a4a714620002ce578063150b7a0214620002e557806326aa101f1462000314578063276fea8a146200033a575b600080fd5b620002cc620002c6366004620041a3565b62000672565b005b620002cc620002df366004620041e1565b620006a8565b620002fc620002f636600462004544565b6200072c565b6040516200030b919062004e06565b60405180910390f35b6200032b62000325366004620041a3565b620007bc565b6040516200030b919062004dfb565b620002cc6200034b366004620048f1565b620007f1565b620002cc62000362366004620041a3565b62000894565b6200037f62000379366004620041a3565b620008c7565b6040516200030b919062004aea565b6200032b6200039f3660046200421e565b6200092d565b6200032b62000ace565b620002fc62000b1f565b620002cc620003ca366004620041a3565b62000b2a565b6200032b620003e1366004620041a3565b62000b5d565b6200032b620003f836600462004819565b62000bf4565b620002cc6200040f366004620041a3565b62000c47565b6200037f62000426366004620041a3565b62000c7a565b620002cc6200043d3660046200432c565b62000ce3565b620002cc620004543660046200462b565b62000d29565b620002cc6200046b366004620041a3565b62000e24565b620002cc620004823660046200432c565b62000e57565b620002cc62001003565b6200037f620010b7565b6200037f6200110e565b620002cc620004b7366004620041a3565b62001165565b6200037f62001198565b620004d1620011ef565b6040516200030b9392919062004e5a565b6200037f620011fa565b6200037f62001251565b620002cc620005073660046200484b565b620012a8565b6200032b6200051e366004620041a3565b62001573565b620002cc62000535366004620043c8565b620015d1565b620002cc6200054c366004620047eb565b62001616565b620002fc6200056336600462004490565b62001623565b6200032b6200057a366004620041a3565b6200170a565b6200037f62001767565b620005a16200059b366004620047eb565b620017be565b6040516200030b919062004e1b565b6200037f620017d6565b620002cc620005cb3660046200498b565b6200182d565b620002cc620005e236600462004660565b6200183f565b620002cc620005f9366004620042c2565b62001929565b620002fc62000610366004620045bc565b62001a72565b620002cc62000627366004620041a3565b62001af0565b6200032b6200063e366004620041a3565b62001b23565b620002cc62000655366004620046d7565b62001bb2565b620002cc6200066c3660046200462b565b62001cef565b6200067c6200110e565b6001600160a01b0316336001600160a01b0316146200069a57600080fd5b620006a58162001de9565b50565b620006b26200110e565b6001600160a01b0316336001600160a01b031614620006d057600080fd5b620006db8162001e68565b620006e557600080fd5b620006f081620007bc565b15620006fb57600080fd5b60006200070883620008c7565b6001600160a01b0316146200071c57600080fd5b62000728828262001e6e565b5050565b60006200073862001f69565b620007a557620007a53386620007858887878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062001f8e92505050565b620007908862001fbb565b60408051600081526020810190915262002000565b506001600160e01b03196000351695945050505050565b6000620007c9826200170a565b80620007e957506000620007dd8362000c7a565b6001600160a01b031614155b90505b919050565b620007fb6200205a565b620008068562000bf4565b156200081157600080fd5b6200081f848484846200210d565b6200082a86620021eb565b146200083557600080fd5b62000840856200223d565b6200084e8484848462002296565b604080516001600160a01b0386168152905186917f1bc59e60074130cba06acb3689c8b1578e753b0ee7f1471c7b9b297fdbd43279919081900360200190a25050505050565b6200089e6200110e565b6001600160a01b0316336001600160a01b031614620008bc57600080fd5b620006a581620022c1565b604080516f686f6d65546f6b656e4164647265737360801b60208083019190915260609390931b6001600160601b03191660308201528151808203602401815260449091018252805190830120600090815260029092529020546001600160a01b031690565b60408051600481526024810182526020810180516001600160e01b03166337ef410160e11b1781529151815160009384936060933093919290918291908083835b602083106200098f5780518252601f1990920191602091820191016200096e565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114620009f1576040519150601f19603f3d011682016040523d82523d6000602084013e620009f6565b606091505b509150915081158062000a3557508051602014801562000a35575080806020019051602081101562000a2757600080fd5b50516001600160a01b031633145b8062000a4057503330145b62000a4a57600080fd5b62000a5462000ace565b1562000a5f57600080fd5b62000a6a8a62001de9565b62000a758962002340565b62000a8088620023aa565b62000a8b876200243b565b62000a9686620022c1565b62000aa18562002506565b62000aac8462002585565b62000ab662002616565b62000ac062000ace565b9a9950505050505050505050565b7f0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba60005260046020527f078d888f9b66f3f8bfa10909e31f1e16240db73449f0500afdbbe3a70da457cc5460ff1690565b63329ff0f760e21b90565b62000b346200110e565b6001600160a01b0316336001600160a01b03161462000b5257600080fd5b620006a58162002506565b6040805170195e1958dd5d1a5bdb911a5cd8589b1959607a1b6020808301919091526001600160601b0319606085901b1660318301528251602581840301815260459092018352815191810191909120600090815260049091529081205460ff16808062000bd257506001600160a01b038316155b1562000be157159050620007ec565b62000bed600062000b5d565b9392505050565b604080516b1b595cdcd859d9519a5e195960a21b602080830191909152602c80830185905283518084039091018152604c909201835281519181019190912060009081526004909152205460ff16919050565b62000c516200110e565b6001600160a01b0316336001600160a01b03161462000c6f57600080fd5b620006a581620023aa565b6040805172666f726569676e546f6b656e4164647265737360681b60208083019190915260609390931b6001600160601b03191660338201528151808203602701815260479091018252805190830120600090815260029092529020546001600160a01b031690565b62000ced6200205a565b62000cf8866200170a565b62000d0257600080fd5b62000d0f8660026200266d565b62000d21866001878787878762002728565b505050505050565b62000d336200110e565b6001600160a01b0316336001600160a01b03161462000d5157600080fd5b6001600160a01b038216158062000d6e575062000d6e82620007bc565b62000d7857600080fd5b6040805170195e1958dd5d1a5bdb911a5cd8589b1959607a1b6020808301919091526001600160601b0319606086901b16603183015282516025818403018152604583018085528151918301919091206000908152600490925290839020805485151560ff199091168117909155905290516001600160a01b038416917f7eaed571869fda346fbb60c26259a8598b4943b8e15079704be1a499d7729055919081900360650190a25050565b62000e2e6200110e565b6001600160a01b0316336001600160a01b03161462000e4c57600080fd5b620006a58162002340565b306001600160a01b0316636fde82026040518163ffffffff1660e01b815260040160206040518083038186803b15801562000e9157600080fd5b505afa15801562000ea6573d6000803e3d6000fd5b505050506040513d602081101562000ebd57600080fd5b50516001600160a01b0316331462000ed457600080fd5b62000edf866200170a565b62000ee957600080fd5b82811462000ef657600080fd5b8262000f0157600080fd5b606062000f74878787878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a9250899182918501908490808284376000920191909152506200281592505050565b9050600062000f8582600162002df8565b905062000ff981898989898080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808d0282810182019093528c82529093508c92508b91829185019084908082843760009201919091525062002f4a92505050565b5050505050505050565b7fa7447530d8540da22352ef04bc2e39e62a1d14ccad54fda9de9532776a4f3201600081905260046020527f0d5b627dac98f5567d4f4279b39c4bc85552d4948f46481aa34373a83ed36cc85460ff16156200105e57600080fd5b6200107d73d3b93549686c857edb0d97b7037bd7d1314c268a620022c1565b6200109c732419697793fa7a582893792a6f9c7391337a878462002506565b6000908152600460205260409020805460ff19166001179055565b7f98aa806e31e94a687a31c65769cb99670064dd7f5a87526da075c5fb4eab988060005260026020527f0c1206883be66049a02d4937078367c00b3d71dd1a9465df969363c6ddeac96d546001600160a01b031690565b7f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c060005260026020527fb7802e97e87ef2842a6cce7da7ffaeaedaa2f61a6a7870b23d9d01fc9b73712e546001600160a01b031690565b6200116f6200110e565b6001600160a01b0316336001600160a01b0316146200118d57600080fd5b620006a58162002585565b7f02e1d283efd236e8b97cefe072f0c863fa2db9f9ba42b0eca53ab31c49067a6760005260026020527f953ab8a758dd553cc38f793a1da5659c9dc6ef69c086214f73ad0b17e262b031546001600160a01b031690565b600360016000909192565b7f5f5bc4e0b888be22a35f2166061a04607296c26861006b9b8e089a172696a82260005260026020527f60072fd9ffad01d76b1d1421ce17a3613dc06795e4b113745995ad1d84a52121546001600160a01b031690565b7f20b8ca26cc94f39fab299954184cf3a9bd04f69543e4f454fab299f015b8130f60005260026020527f0c25bd6cb8545e46227bc56e841bd085538c74351fb22e98acdfbe596b353cfb546001600160a01b031690565b82620012b357600080fd5b801580620012c057508281145b620012ca57600080fd5b6000620012d6620017d6565b9050806001600160a01b031663cb08a10c896040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156200131d57600080fd5b505afa15801562001332573d6000803e3d6000fd5b505050506040513d60208110156200134957600080fd5b5051156200135657600080fd5b306001600160a01b0316816001600160a01b0316633f9a8e7e8a6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015620013a557600080fd5b505afa158015620013ba573d6000803e3d6000fd5b505050506040513d6020811015620013d157600080fd5b50516001600160a01b031614620013e757600080fd5b620013f1620010b7565b6001600160a01b0316816001600160a01b0316634a610b048a6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156200143f57600080fd5b505afa15801562001454573d6000803e3d6000fd5b505050506040513d60208110156200146b57600080fd5b50516001600160a01b0316146200148157600080fd5b606063276fea8a60e01b8989898989898960405160240180888152602001876001600160a01b03168152602001866001600160a01b0316815260200180602001806020018381038352878782818152602001925060200280828437600083820152601f01601f19169091018481038352858152602090810191508690860280828437600081840152601f19601f8201169050808301925050509950505050505050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b03838183161783525050505090506200156781600062002df8565b50505050505050505050565b604080516e1d1bdad95b949959da5cdd195c9959608a1b60208083019190915260609390931b6001600160601b031916602f820152815180820360230181526043909101825280519083012060009081529182905290205460021490565b620015db6200205a565b6000620015e889620008c7565b9050620015fc8160008a8a8a8a8a62002728565b6200160b818888868662002fcf565b505050505050505050565b62000728823383620030a9565b60008584146200163257600080fd5b856200163d57600080fd5b620016f03389620016858b87878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062001f8e92505050565b8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152506200200092505050565b506001600160e01b03196000351698975050505050505050565b604080516e1d1bdad95b949959da5cdd195c9959608a1b60208083019190915260609390931b6001600160601b031916602f8201528151808203602301815260439091018252805190830120600090815291829052902054151590565b7f5f86f226cd489cc09187d5f5e0adfb94308af0d4ceac482dd8a8adea9d80daf460005260026020527fab9e97adef29adb9492a44df89badb4a706f8f35202918df21ca61ed056c4868546001600160a01b031690565b600080620017cd848462003156565b54949350505050565b7f811bbb11e8899da471f0e69a3ed55090fc90215227fc5fb1cb0d6e962ea7b74f60005260026020527fb4ed64697d3ef8518241966f7c6f28b0d72f20f51198717d198d2d55076c593d546001600160a01b031690565b6200183a838383620030a9565b505050565b620018496200110e565b6001600160a01b0316336001600160a01b0316146200186757600080fd5b6040805169637573746f6d4e616d6560b01b6020808301919091526001600160601b0319606089901b16602a8301528251601e818403018152603e90920183528151918101919091206000908152600190915220620018c890858562003f52565b50604080516b18dd5cdd1bdb54de5b589bdb60a21b6020808301919091526001600160601b0319606089901b16602c8301528251808303820181529183018352815191810191909120600090815260019091522062000d2190838362003f52565b306001600160a01b0316636fde82026040518163ffffffff1660e01b815260040160206040518083038186803b1580156200196357600080fd5b505afa15801562001978573d6000803e3d6000fd5b505050506040513d60208110156200198f57600080fd5b50516001600160a01b03163314620019a657600080fd5b620019b1846200170a565b620019bb57600080fd5b80620019c657600080fd5b6040805160008152602083810280830184018452908201848152919260609262001a139289928992918991899182918a019084908082843760009201919091525088925062002815915050565b9050600062001a2482600162002df8565b905062001a698188888888808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508a925062002f4a915050565b50505050505050565b600062001ad8338762001abc8987878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062001f8e92505050565b62001ac78962001fbb565b62001ad28962001fbb565b62002000565b506001600160e01b0319600035169695505050505050565b62001afa6200110e565b6001600160a01b0316336001600160a01b03161462001b1857600080fd5b620006a5816200243b565b604080516f189c9a5919da5b99d11a5cd8589b195960821b6020808301919091526001600160601b0319606085901b1660308301528251602481840301815260449092018352815191810191909120600090815260049091529081205460ff16808062001b9757506001600160a01b038316155b1562001ba657159050620007ec565b62000bed600062001b23565b62001bbc6200205a565b600062001bc98b620008c7565b90506001600160a01b03811662001cc157895162001bfd5788511562001bf75762001bf489620031ce565b99505b62001c16565b885162001c08578998505b62001c138a620031ce565b99505b8362001c6a5762001c2662001251565b8a8a3060405162001c379062003fe7565b62001c46949392919062004cbe565b604051809103906000f08015801562001c63573d6000803e3d6000fd5b5062001cb3565b62001c7462001198565b8a8a3060405162001c859062003ff5565b62001c94949392919062004cbe565b604051809103906000f08015801562001cb1573d6000803e3d6000fd5b505b905062001cc18b8262001e6e565b62001cd38160008a8a8a8a8a62002728565b62001ce2818888868662002fcf565b5050505050505050505050565b62001cf96200110e565b6001600160a01b0316336001600160a01b03161462001d1757600080fd5b6001600160a01b038216158062001d34575062001d3482620007bc565b62001d3e57600080fd5b604080516f189c9a5919da5b99d11a5cd8589b195960821b6020808301919091526001600160601b0319606086901b16603083015282516024818403018152604483018085528151918301919091206000908152600490925290839020805485151560ff199091168117909155905290516001600160a01b038416917fbeecfafc3eb3de7b89bd034a6a32944da5b2e4972cc6875eaa57b0727642188f919081900360640190a25050565b62001df48162001e68565b62001dfe57600080fd5b7f811bbb11e8899da471f0e69a3ed55090fc90215227fc5fb1cb0d6e962ea7b74f60005260026020527fb4ed64697d3ef8518241966f7c6f28b0d72f20f51198717d198d2d55076c593d80546001600160a01b0319166001600160a01b0392909216919091179055565b3b151590565b604080516f686f6d65546f6b656e4164647265737360801b6020808301919091526001600160601b0319606086811b82166030850152845160248186030181526044850186528051908401206000908152600280855286822080546001600160a01b03808b166001600160a01b0319928316811790935572666f726569676e546f6b656e4164647265737360681b60648a0152948a901b90951660778801528751606b818903018152608b909701808952875197870197909720835294529485208054909216908716908117909155909290917f78d063210f4fb6b4cc932390bb8045fa2465e51349590182dab8b9e84c57a6ee9190a35050565b7f6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e925490565b805182901562001fb557815160141462001fa757600080fd5b62001fb28262003243565b90505b92915050565b60408051600180825281830190925260609182919060208083019080368337019050509050828160008151811062001fef57fe5b602090810291909101015292915050565b6200200b85620007bc565b6200201d576200201d8560016200266d565b60606200202d8685858562002815565b905060006200204982620020438989896200324a565b62002df8565b905062001a69818888878762002f4a565b600062002066620017d6565b9050336001600160a01b038216146200207e57600080fd5b62002088620010b7565b6001600160a01b0316816001600160a01b031663d67bdd256040518163ffffffff1660e01b815260040160206040518083038186803b158015620020cb57600080fd5b505afa158015620020e0573d6000803e3d6000fd5b505050506040513d6020811015620020f757600080fd5b50516001600160a01b031614620006a557600080fd5b60008484848460405160200180856001600160a01b03168152602001846001600160a01b031681526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156200217a57818101518382015260200162002160565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015620021bb578181015183820152602001620021a1565b5050505090500196505050505050506040516020818303038152906040528051906020012090505b949350505050565b604080516e6d657373616765436865636b73756d60881b602080830191909152602f80830185905283518084039091018152604f90920183528151918101919091206000908152908190522054919050565b604080516b1b595cdcd859d9519a5e195960a21b602080830191909152602c8083019490945282518083039094018452604c9091018252825192810192909220600090815260049092529020805460ff19166001179055565b620022bb846000620022a88262000c7a565b6001600160a01b031614858585620032ff565b50505050565b620022cc8162001e68565b620022d657600080fd5b7f20b8ca26cc94f39fab299954184cf3a9bd04f69543e4f454fab299f015b8130f60005260026020527f0c25bd6cb8545e46227bc56e841bd085538c74351fb22e98acdfbe596b353cfb80546001600160a01b0319166001600160a01b0392909216919091179055565b7f98aa806e31e94a687a31c65769cb99670064dd7f5a87526da075c5fb4eab988060005260026020527f0c1206883be66049a02d4937078367c00b3d71dd1a9465df969363c6ddeac96d80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381161580620023c75750620023c78162001e68565b620023d157600080fd5b7f5f5bc4e0b888be22a35f2166061a04607296c26861006b9b8e089a172696a82260005260026020527f60072fd9ffad01d76b1d1421ce17a3613dc06795e4b113745995ad1d84a5212180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166200244f57600080fd5b7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06200247a6200110e565b604080516001600160a01b03928316815291841660208301528051918290030190a17f02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c060005260026020527fb7802e97e87ef2842a6cce7da7ffaeaedaa2f61a6a7870b23d9d01fc9b73712e80546001600160a01b0319166001600160a01b0392909216919091179055565b620025118162001e68565b6200251b57600080fd5b7f02e1d283efd236e8b97cefe072f0c863fa2db9f9ba42b0eca53ab31c49067a6760005260026020527f953ab8a758dd553cc38f793a1da5659c9dc6ef69c086214f73ad0b17e262b03180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381161580620025a25750620025a28162001e68565b620025ac57600080fd5b7f5f86f226cd489cc09187d5f5e0adfb94308af0d4ceac482dd8a8adea9d80daf460005260026020527fab9e97adef29adb9492a44df89badb4a706f8f35202918df21ca61ed056c486880546001600160a01b0319166001600160a01b0392909216919091179055565b7f0a6f646cd611241d8073675e00d1a1ff700fbf1b53fcf473de56d1e6e4b714ba60005260046020527f078d888f9b66f3f8bfa10909e31f1e16240db73449f0500afdbbe3a70da457cc805460ff19166001179055565b604080516e1d1bdad95b949959da5cdd195c9959608a1b6020808301919091526001600160601b0319606086901b16602f8301528251602381840301815260439092018352815191810191909120600090815290819052205481146200072857604080516e1d1bdad95b949959da5cdd195c9959608a1b60208083019190915260609490941b6001600160601b031916602f820152815180820360230181526043909101825280519084012060009081529283905290912055565b620027338762000b5d565b6200273d57600080fd5b620027af87878787878080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808b0282810182019093528a82529093508a925089918291850190849080828437600092019190915250620032ff92505050565b620027b9620035be565b856001600160a01b0316886001600160a01b03167fdba5aae9a9fef2121c9cd75845616e9a0ab4820000f44140995e6c286ef495db8787878760405162002804949392919062004da7565b60405180910390a450505050505050565b60606001600160a01b038416158015906200284b575062002835620010b7565b6001600160a01b0316846001600160a01b031614155b6200285557600080fd5b6000620028628662000c7a565b90506001600160a01b03811662002c9557606084516001600160401b03811180156200288d57600080fd5b50604051908082528060200260200182016040528015620028c357816020015b6060815260200190600190039081620028ad5790505b5084519091501562002a3c5760005b855181101562002a35576000620028fe89888481518110620028f057fe5b6020026020010151620017be565b905060006200292b8784815181106200291357fe5b6020026020010151836200363690919063ffffffff16565b9050808a6001600160a01b031662fdd58e308b87815181106200294a57fe5b60200260200101516040518363ffffffff1660e01b81526004016200297192919062004d8e565b60206040518083038186803b1580156200298a57600080fd5b505afa1580156200299f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620029c5919062004832565b1015620029d157600080fd5b620029f28a898581518110620029e357fe5b60200260200101518362003691565b62002a128a89858151811062002a0457fe5b6020026020010151620036a8565b84848151811062002a1f57fe5b60209081029190910101525050600101620028d2565b5062002b7f565b60005b855181101562002b7d5762002a5b88878381518110620028f057fe5b1562002a6657600080fd5b306001600160a01b0316886001600160a01b0316636352211e88848151811062002a8c57fe5b60200260200101516040518263ffffffff1660e01b815260040162002ab2919062004e1b565b60206040518083038186803b15801562002acb57600080fd5b505afa15801562002ae0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002b069190620041c2565b6001600160a01b03161462002b1a57600080fd5b62002b3c8887838151811062002b2c57fe5b6020026020010151600162003691565b62002b5c8887838151811062002b4e57fe5b60200260200101516200387f565b82828151811062002b6957fe5b602090810291909101015260010162002a3f565b505b62002b8a8762001573565b1562002c015760138551111562002ba057600080fd5b604051632dc0782560e21b9062002bc4908990899089908990879060240162004b6e565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091529250620021e3915050565b600e8551111562002c1157600080fd5b606062002c1e88620038f8565b9050606062002c2d8962003b8f565b905063f92d746860e01b8983838b8b8b8960405160240162002c56979695949392919062004d0b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091529450620021e39350505050565b82511562002d0757604051637265da3760e11b81526001600160a01b0387169063e4cbb46e9062002ccd908790879060040162004dd2565b600060405180830381600087803b15801562002ce857600080fd5b505af115801562002cfd573d6000803e3d6000fd5b5050505062002d99565b60005b845181101562002d9757866001600160a01b03166342966c6886838151811062002d3057fe5b60200260200101516040518263ffffffff1660e01b815260040162002d56919062004e1b565b600060405180830381600087803b15801562002d7157600080fd5b505af115801562002d86573d6000803e3d6000fd5b50506001909201915062002d0a9050565b505b604051636ca4835760e01b9062002dbb90839088908890889060240162004b21565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152915050949350505050565b60008062002e05620010b7565b9050600062002e148562003d34565b9050600062002e22620017d6565b90508462002eb8576040516394643f7160e01b81526001600160a01b038216906394643f719062002e5c9086908a90879060040162004c88565b602060405180830381600087803b15801562002e7757600080fd5b505af115801562002e8c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002eb2919062004832565b62002f40565b60405163dc8601b360e01b81526001600160a01b0382169063dc8601b39062002eea9086908a90879060040162004c88565b602060405180830381600087803b15801562002f0557600080fd5b505af115801562002f1a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002f40919062004832565b9695505050505050565b62002f558462001b23565b62002f5f57600080fd5b62002f788562002f72868686866200210d565b62003e39565b84836001600160a01b0316856001600160a01b03167f4592bc44d36f208ac23b24a332824c332066017d3978a7d3c6543f992ea17f3d858560405162002fc092919062004dd2565b60405180910390a45050505050565b60005b8381101562000d2157600083838381811062002fea57fe5b905060200281019062002ffe919062004e7d565b90501115620030a057856001600160a01b031663162094c48686848181106200302357fe5b905060200201358585858181106200303757fe5b90506020028101906200304b919062004e7d565b6040518463ffffffff1660e01b81526004016200306b9392919062004e24565b600060405180830381600087803b1580156200308657600080fd5b505af11580156200309b573d6000803e3d6000fd5b505050505b60010162002fd2565b620030b362001f69565b15620030be57600080fd5b620030ca600162003e8b565b604080516323b872dd60e01b81523360048201523060248201526044810183905290516001600160a01b038516916323b872dd91606480830192600092919082900301818387803b1580156200311f57600080fd5b505af115801562003134573d6000803e3d6000fd5b5050505062003144600062003e8b565b6200183a833384620007908562001fbb565b604080516b6d65646961746f724f776e7360a01b602080830191909152606094851b6001600160601b031916602c8301528183019390935281518082038301815293810182528351938301939093206080840152600460a0808501919091528151808503909101815260c09093019052815191012090565b606080827f66726f6d204b6f76616e000000000000000000000000000000000000000000006040516020016200320692919062004ac6565b60408051808303601f1901815291905292517f000000000000000000000000000000000000000000000000000000000000000a0183525090919050565b6014015190565b6000806200325762001767565b90506001600160a01b0381161580620032f6575060405163f7baa04960e01b81526000906001600160a01b0383169063f7baa04990620032a09089908990899060040162004afe565b60206040518083038186803b158015620032b957600080fd5b505afa158015620032ce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620032f4919062004832565b135b95945050505050565b80511562003461578315620033f45760005b825181101562003376576200336d868483815181106200332d57fe5b6020026020010151620033678585815181106200334657fe5b6020026020010151620033608b898881518110620028f057fe5b9062003eaf565b62003691565b60010162003311565b5060408051600081526020810191829052631759616b60e11b9091526001600160a01b03861690632eb2c2d690620033ba9030908790879087906024810162004bd2565b600060405180830381600087803b158015620033d557600080fd5b505af1158015620033ea573d6000803e3d6000fd5b505050506200345b565b604051634b93bab560e11b81526001600160a01b03861690639727756a90620034269086908690869060040162004c4e565b600060405180830381600087803b1580156200344157600080fd5b505af115801562003456573d6000803e3d6000fd5b505050505b620035b7565b8315620035255760005b82518110156200351e5762003497868483815181106200348757fe5b6020026020010151600062003691565b856001600160a01b03166323b872dd3086868581518110620034b557fe5b60200260200101516040518463ffffffff1660e01b8152600401620034dd9392919062004c2a565b600060405180830381600087803b158015620034f857600080fd5b505af11580156200350d573d6000803e3d6000fd5b5050600190920191506200346b9050565b50620035b7565b60005b825181101562000d2157856001600160a01b03166340c10f19858584815181106200354f57fe5b60200260200101516040518363ffffffff1660e01b81526004016200357692919062004d8e565b600060405180830381600087803b1580156200359157600080fd5b505af1158015620035a6573d6000803e3d6000fd5b505060019092019150620035289050565b5050505050565b6000620035ca620017d6565b6001600160a01b031663669f618b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200360357600080fd5b505afa15801562003618573d6000803e3d6000fd5b505050506040513d60208110156200362f57600080fd5b5051905090565b60008282018381101562001fb2576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60006200369f848462003156565b91909155505050565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b03166303a24d0760e21b1781529151815160609360009385936001600160a01b03891693919290918291908083835b60208310620037225780518252601f19909201916020918201910162003701565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811462003784576040519150601f19603f3d011682016040523d82523d6000602084013e62003789565b606091505b509150915081620037aa5760405180602001604052806000815250620032f6565b808060200190516020811015620037c057600080fd5b8101908080516040519392919084640100000000821115620037e157600080fd5b908301906020820185811115620037f757600080fd5b82516401000000008111828201881017156200381257600080fd5b82525081516020918201929091019080838360005b838110156200384157818101518382015260200162003827565b50505050905090810190601f1680156200386f5780820380516001836020036101000a031916815260200191505b5060405250505095945050505050565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b031663c87b56dd60e01b1781529151815160609360009385936001600160a01b038916939192909182919080838360208310620037225780518252601f19909201916020918201910162003701565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b1781529151815160609360009385936001600160a01b03881693919290918291908083835b60208310620039635780518252601f19909201916020918201910162003942565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114620039c5576040519150601f19603f3d011682016040523d82523d6000602084013e620039ca565b606091505b50915091508162003abb576040805169637573746f6d4e616d6560b01b602080830191909152606087901b6001600160601b031916602a8301528251808303601e018152603e83018085528151918301919091206000908152600180845290859020805460029281161561010002600019011691909104601f81018490049093028401605e90810190955282825290939092018282801562003ab05780601f1062003a845761010080835404028352916020019162003ab0565b820191906000526020600020905b81548152906001019060200180831162003a9257829003601f168201915b5050505050620021e3565b80806020019051602081101562003ad157600080fd5b810190808051604051939291908464010000000082111562003af257600080fd5b90830190602082018581111562003b0857600080fd5b825164010000000081118282018810171562003b2357600080fd5b82525081516020918201929091019080838360005b8381101562003b5257818101518382015260200162003b38565b50505050905090810190601f16801562003b805780820380516001836020036101000a031916815260200191505b50604052505050949350505050565b60408051600481526024810182526020810180516001600160e01b03166395d89b4160e01b1781529151815160609360009385936001600160a01b03881693919290918291908083835b6020831062003bfa5780518252601f19909201916020918201910162003bd9565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811462003c5c576040519150601f19603f3d011682016040523d82523d6000602084013e62003c61565b606091505b50915091508162003abb57600160008560405160200180806b18dd5cdd1bdb54de5b589bdb60a21b815250600c01826001600160a01b031660601b81526014019150506040516020818303038152906040528051906020012081526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801562003ab05780601f1062003a845761010080835404028352916020019162003ab0565b60008062003d41620011fa565b90506001600160a01b0381161562003e2f5760405163fb47201960e01b81526020600482018181528551602484015285516001600160a01b0385169363fb4720199388939283926044019185019080838360005b8381101562003daf57818101518382015260200162003d95565b50505050905090810190601f16801562003ddd5780820380516001836020036101000a031916815260200191505b509250505060206040518083038186803b15801562003dfb57600080fd5b505afa15801562003e10573d6000803e3d6000fd5b505050506040513d602081101562003e2757600080fd5b505162000bed565b62000bed62003f0d565b604080516e6d657373616765436865636b73756d60881b602080830191909152602f8083019590955282518083039095018552604f909101825283519381019390932060009081529283905290912055565b7f6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e9255565b60008282111562003f07576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600062003f19620017d6565b6001600160a01b031663e5789d036040518163ffffffff1660e01b815260040160206040518083038186803b1580156200360357600080fd5b828054600181600116156101000203166002900490600052602060002090601f01602090048101928262003f8a576000855562003fd5565b82601f1062003fa55782800160ff1982351617855562003fd5565b8280016001018555821562003fd5579182015b8281111562003fd557823582559160200191906001019062003fb8565b5062003fe392915062004003565b5090565b61053a8062004f2e83390190565b61053a806200546883390190565b5b8082111562003fe3576000815560010162004004565b8035620007ec8162004f17565b60008083601f84011262004039578182fd5b5081356001600160401b0381111562004050578182fd5b60208301915083602080830285010111156200406b57600080fd5b9250929050565b600082601f83011262004083578081fd5b81356001600160401b038111156200409757fe5b6020808202620040a982820162004ec4565b83815293508184018583018287018401881015620040c657600080fd5b600092505b84831015620040eb578035825260019290920191908301908301620040cb565b505050505092915050565b60008083601f84011262004108578182fd5b5081356001600160401b038111156200411f578182fd5b6020830191508360208285010111156200406b57600080fd5b600082601f83011262004149578081fd5b81356001600160401b038111156200415d57fe5b62004172601f8201601f191660200162004ec4565b91508082528360208285010111156200418a57600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215620041b5578081fd5b813562001fb28162004f17565b600060208284031215620041d4578081fd5b815162001fb28162004f17565b60008060408385031215620041f4578081fd5b8235620042018162004f17565b91506020830135620042138162004f17565b809150509250929050565b600080600080600080600060e0888a03121562004239578283fd5b8735620042468162004f17565b96506020880135620042588162004f17565b955060408801356200426a8162004f17565b945060608801356200427c8162004f17565b935060808801356200428e8162004f17565b925060a0880135620042a08162004f17565b915060c0880135620042b28162004f17565b8091505092959891949750929550565b60008060008060608587031215620042d8578384fd5b8435620042e58162004f17565b93506020850135620042f78162004f17565b925060408501356001600160401b0381111562004312578283fd5b620043208782880162004027565b95989497509550505050565b6000806000806000806080878903121562004345578182fd5b8635620043528162004f17565b95506020870135620043648162004f17565b945060408701356001600160401b038082111562004380578384fd5b6200438e8a838b0162004027565b90965094506060890135915080821115620043a7578384fd5b50620043b689828a0162004027565b979a9699509497509295939492505050565b60008060008060008060008060a0898b031215620043e4578081fd5b8835620043f18162004f17565b97506020890135620044038162004f17565b965060408901356001600160401b03808211156200441f578283fd5b6200442d8c838d0162004027565b909850965060608b013591508082111562004446578283fd5b620044548c838d0162004027565b909650945060808b01359150808211156200446d578283fd5b506200447c8b828c0162004027565b999c989b5096995094979396929594505050565b60008060008060008060008060a0898b031215620044ac578182fd5b8835620044b98162004f17565b97506020890135620044cb8162004f17565b965060408901356001600160401b0380821115620044e7578384fd5b620044f58c838d0162004027565b909850965060608b01359150808211156200450e578384fd5b6200451c8c838d0162004027565b909650945060808b013591508082111562004535578384fd5b506200447c8b828c01620040f6565b6000806000806000608086880312156200455c578283fd5b8535620045698162004f17565b945060208601356200457b8162004f17565b93506040860135925060608601356001600160401b038111156200459d578182fd5b620045ab88828901620040f6565b969995985093965092949392505050565b60008060008060008060a08789031215620045d5578384fd5b8635620045e28162004f17565b95506020870135620045f48162004f17565b9450604087013593506060870135925060808701356001600160401b038111156200461d578283fd5b620043b689828a01620040f6565b600080604083850312156200463e578182fd5b82356200464b8162004f17565b91506020830135801515811462004213578182fd5b60008060008060006060868803121562004678578283fd5b8535620046858162004f17565b945060208601356001600160401b0380821115620046a1578485fd5b620046af89838a01620040f6565b90965094506040880135915080821115620046c8578283fd5b50620045ab88828901620040f6565b60008060008060008060008060008060e08b8d031215620046f6578384fd5b620047018b6200401a565b995060208b01356001600160401b03808211156200471d578586fd5b6200472b8e838f0162004138565b9a5060408d013591508082111562004741578586fd5b6200474f8e838f0162004138565b99506200475f60608e016200401a565b985060808d013591508082111562004775578586fd5b620047838e838f0162004027565b909850965060a08d01359150808211156200479c578586fd5b620047aa8e838f0162004027565b909650945060c08d0135915080821115620047c3578384fd5b50620047d28d828e0162004027565b915080935050809150509295989b9194979a5092959850565b60008060408385031215620047fe578182fd5b82356200480b8162004f17565b946020939093013593505050565b6000602082840312156200482b578081fd5b5035919050565b60006020828403121562004844578081fd5b5051919050565b600080600080600080600060a0888a03121562004866578081fd5b8735965060208801356200487a8162004f17565b955060408801356200488c8162004f17565b945060608801356001600160401b0380821115620048a8578283fd5b620048b68b838c0162004027565b909650945060808a0135915080821115620048cf578283fd5b50620048de8a828b0162004027565b989b979a50959850939692959293505050565b600080600080600060a0868803121562004909578283fd5b8535945060208601356200491d8162004f17565b935060408601356200492f8162004f17565b925060608601356001600160401b03808211156200494b578283fd5b6200495989838a0162004072565b935060808801359150808211156200496f578283fd5b506200497e8882890162004072565b9150509295509295909350565b600080600060608486031215620049a0578081fd5b8335620049ad8162004f17565b92506020840135620049bf8162004f17565b929592945050506040919091013590565b6000815180845260208085018081965082840281019150828601855b8581101562004a1a57828403895262004a0784835162004a98565b98850198935090840190600101620049ec565b5091979650505050505050565b81835260006001600160fb1b0383111562004a40578081fd5b6020830280836020870137939093016020019283525090919050565b6000815180845260208085019450808401835b8381101562004a8d5781518752958201959082019060010162004a6f565b509495945050505050565b6000815180845262004ab281602086016020860162004ee8565b601f01601f19169290920160200192915050565b6000835162004ada81846020880162004ee8565b9190910191825250602001919050565b6001600160a01b0391909116815260200190565b6001600160a01b0393841681529183166020830152909116604082015260600190565b6001600160a01b0385811682528416602082015260806040820181905260009062004b4f9083018562004a5c565b828103606084015262004b63818562004a5c565b979650505050505050565b6001600160a01b0386811682528516602082015260a06040820181905260009062004b9c9083018662004a5c565b828103606084015262004bb0818662004a5c565b9050828103608084015262004bc68185620049d0565b98975050505050505050565b6001600160a01b0386811682528516602082015260a06040820181905260009062004c009083018662004a5c565b828103606084015262004c14818662004a5c565b9050828103608084015262004bc6818562004a98565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b038416815260606020820181905260009062004c749083018562004a5c565b828103604084015262002f40818562004a5c565b6001600160a01b038416815260606020820181905260009062004cae9083018562004a98565b9050826040830152949350505050565b600060018060a01b0380871683526080602084015262004ce2608084018762004a98565b838103604085015262004cf6818762004a98565b92505080841660608401525095945050505050565b600060018060a01b03808a16835260e0602084015262004d2f60e084018a62004a98565b838103604085015262004d43818a62004a98565b90508188166060850152838103608085015262004d61818862004a5c565b91505082810360a084015262004d78818662004a5c565b905082810360c084015262000ac08185620049d0565b6001600160a01b03929092168252602082015260400190565b60006040825262004dbd60408301868862004a27565b828103602084015262004b6381858762004a27565b60006040825262004de7604083018562004a5c565b8281036020840152620032f6818562004a5c565b901515815260200190565b6001600160e01b031991909116815260200190565b90815260200190565b60008482526040602083015282604083015282846060840137818301606090810191909152601f909201601f1916010192915050565b6001600160401b0393841681529183166020830152909116604082015260600190565b6000808335601e1984360301811262004e94578283fd5b8301803591506001600160401b0382111562004eae578283fd5b6020019150368190038213156200406b57600080fd5b6040518181016001600160401b038111828210171562004ee057fe5b604052919050565b60005b8381101562004f0557818101518382015260200162004eeb565b83811115620022bb5750506000910152565b6001600160a01b0381168114620006a557600080fdfe608060405234801561001057600080fd5b5060405161053a38038061053a8339818101604052608081101561003357600080fd5b81516020830180516040519294929383019291908464010000000082111561005a57600080fd5b90830190602082018581111561006f57600080fd5b825164010000000081118282018810171561008957600080fd5b82525081516020918201929091019080838360005b838110156100b657818101518382015260200161009e565b50505050905090810190601f1680156100e35780820380516001836020036101000a031916815260200191505b506040526020018051604051939291908464010000000082111561010657600080fd5b90830190602082018581111561011b57600080fd5b825164010000000081118282018810171561013557600080fd5b82525081516020918201929091019080838360005b8381101561016257818101518382015260200161014a565b50505050905090810190601f16801561018f5780820380516001836020036101000a031916815260200191505b506040526020908101517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc87905585519093506101d29250600691860190610210565b5081516101e6906007906020850190610210565b50600a80546001600160a01b0319166001600160a01b0392909216919091179055506102b1915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282610246576000855561028c565b82601f1061025f57805160ff191683800117855561028c565b8280016001018555821561028c579182015b8281111561028c578251825591602001919060010190610271565b5061029892915061029c565b5090565b5b80821115610298576000815560010161029d565b61027a806102c06000396000f3fe6080604052600436106100345760003560e01c80632f95b6aa1461007d5780635c60da1b146100be578063d784d426146100ef575b600061003e610124565b90506001600160a01b03811661005357600080fd5b60405136600082376000803683855af43d82016040523d6000833e808015610079573d83f35b3d83fd5b34801561008957600080fd5b50610092610149565b6040805167ffffffffffffffff9485168152928416602084015292168183015290519081900360600190f35b3480156100ca57600080fd5b506100d3610124565b604080516001600160a01b039092168252519081900360200190f35b3480156100fb57600080fd5b506101226004803603602081101561011257600080fd5b50356001600160a01b0316610152565b005b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b60016000809192565b600a546001600160a01b03163314806101ec5750600a60009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156101b457600080fd5b505afa1580156101c8573d6000803e3d6000fd5b505050506040513d60208110156101de57600080fd5b50516001600160a01b031633145b6101f557600080fd5b6001600160a01b03811661020857600080fd5b6102118161023e565b61021a57600080fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b3b15159056fea2646970667358221220bc88bfc34175d5f654e94d2bf5d87e430cfb86f6b1fd721c819c7b4541dfff6264736f6c63430007050033608060405234801561001057600080fd5b5060405161053a38038061053a8339818101604052608081101561003357600080fd5b81516020830180516040519294929383019291908464010000000082111561005a57600080fd5b90830190602082018581111561006f57600080fd5b825164010000000081118282018810171561008957600080fd5b82525081516020918201929091019080838360005b838110156100b657818101518382015260200161009e565b50505050905090810190601f1680156100e35780820380516001836020036101000a031916815260200191505b506040526020018051604051939291908464010000000082111561010657600080fd5b90830190602082018581111561011b57600080fd5b825164010000000081118282018810171561013557600080fd5b82525081516020918201929091019080838360005b8381101561016257818101518382015260200161014a565b50505050905090810190601f16801561018f5780820380516001836020036101000a031916815260200191505b506040526020908101517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc87905585519093506101d29250600491860190610210565b5081516101e6906005906020850190610210565b50600880546001600160a01b0319166001600160a01b0392909216919091179055506102b1915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282610246576000855561028c565b82601f1061025f57805160ff191683800117855561028c565b8280016001018555821561028c579182015b8281111561028c578251825591602001919060010190610271565b5061029892915061029c565b5090565b5b80821115610298576000815560010161029d565b61027a806102c06000396000f3fe6080604052600436106100345760003560e01c80632f95b6aa1461007d5780635c60da1b146100be578063d784d426146100ef575b600061003e610124565b90506001600160a01b03811661005357600080fd5b60405136600082376000803683855af43d82016040523d6000833e808015610079573d83f35b3d83fd5b34801561008957600080fd5b50610092610149565b6040805167ffffffffffffffff9485168152928416602084015292168183015290519081900360600190f35b3480156100ca57600080fd5b506100d3610124565b604080516001600160a01b039092168252519081900360200190f35b3480156100fb57600080fd5b506101226004803603602081101561011257600080fd5b50356001600160a01b0316610152565b005b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b60016000809192565b6008546001600160a01b03163314806101ec5750600860009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156101b457600080fd5b505afa1580156101c8573d6000803e3d6000fd5b505050506040513d60208110156101de57600080fd5b50516001600160a01b031633145b6101f557600080fd5b6001600160a01b03811661020857600080fd5b6102118161023e565b61021a57600080fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b3b15159056fea2646970667358221220172e5b97c48bf1ecec2125e7f83d61c8663c5d22fba42b7e8eee56182c804c1564736f6c63430007050033a2646970667358221220289324b4a52fcf92a6a5a39ae999c316874c87177a256b7fb53ba353eebde28864736f6c63430007050033