ETH Price: $2,684.79 (-2.54%)

Contract

0xa4E177abEAd6758567eF78FFC150741187838cAc
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Mint Multiple145965542022-04-16 13:17:12863 days ago1650115032IN
0xa4E177ab...187838cAc
0 ETH0.0046591819.78256155
Mint Multiple145787172022-04-13 18:21:05866 days ago1649874065IN
0xa4E177ab...187838cAc
0 ETH0.0204129741.08164084
Mint Multiple145761812022-04-13 8:50:08866 days ago1649839808IN
0xa4E177ab...187838cAc
0 ETH0.0245355237.42984442
Mint145761322022-04-13 8:38:09866 days ago1649839089IN
0xa4E177ab...187838cAc
0 ETH0.0071206935.38795418
Mint145751612022-04-13 4:44:05866 days ago1649825045IN
0xa4E177ab...187838cAc
0 ETH0.0106159951.20928389
Mint145751322022-04-13 4:38:30866 days ago1649824710IN
0xa4E177ab...187838cAc
0 ETH0.0098457745.3919199
Mint145751302022-04-13 4:37:37866 days ago1649824657IN
0xa4E177ab...187838cAc
0 ETH0.0078493936.1879958
Mint145751252022-04-13 4:35:43866 days ago1649824543IN
0xa4E177ab...187838cAc
0 ETH0.0073195733.74535652
Mint145726702022-04-12 19:33:31867 days ago1649792011IN
0xa4E177ab...187838cAc
0 ETH0.0099110646.97923851
Mint145706922022-04-12 12:29:36867 days ago1649766576IN
0xa4E177ab...187838cAc
0 ETH0.0074060536.78107486
Mint Multiple145701422022-04-12 10:19:47867 days ago1649758787IN
0xa4E177ab...187838cAc
0 ETH0.0153201937.72184078
Mint145693932022-04-12 7:32:32867 days ago1649748752IN
0xa4E177ab...187838cAc
0 ETH0.0077303736.64257351
Mint145693772022-04-12 7:27:50867 days ago1649748470IN
0xa4E177ab...187838cAc
0 ETH0.0070166433.81352917
Mint145693672022-04-12 7:25:38867 days ago1649748338IN
0xa4E177ab...187838cAc
0 ETH0.007010734.83927476
Mint145643152022-04-11 12:06:44868 days ago1649678804IN
0xa4E177ab...187838cAc
0 ETH0.007766234.37801693
Mint145643122022-04-11 12:06:01868 days ago1649678761IN
0xa4E177ab...187838cAc
0 ETH0.0074788733.10787706
Mint145643012022-04-11 12:04:09868 days ago1649678649IN
0xa4E177ab...187838cAc
0 ETH0.007997835.40510535
Mint145642912022-04-11 12:02:02868 days ago1649678522IN
0xa4E177ab...187838cAc
0 ETH0.0080158335.48491795
Mint Multiple145576762022-04-10 11:23:08869 days ago1649589788IN
0xa4E177ab...187838cAc
0 ETH0.005901222.57199624
Mint Multiple145514282022-04-09 12:11:02870 days ago1649506262IN
0xa4E177ab...187838cAc
0 ETH0.0255390243.53292985
Mint Multiple145340772022-04-06 19:07:47873 days ago1649272067IN
0xa4E177ab...187838cAc
0 ETH0.0224947645.67112426
Mint Multiple145329562022-04-06 14:47:26873 days ago1649256446IN
0xa4E177ab...187838cAc
0 ETH0.0018361140.46531592
Mint Multiple145329542022-04-06 14:47:15873 days ago1649256435IN
0xa4E177ab...187838cAc
0 ETH0.0023652752.12718124
Mint Multiple145274372022-04-05 17:59:26874 days ago1649181566IN
0xa4E177ab...187838cAc
0 ETH0.0286627171.13977062
Mint Multiple145274012022-04-05 17:50:15874 days ago1649181015IN
0xa4E177ab...187838cAc
0 ETH0.0892504283.49167138
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CatalystMinter

Compiler Version
v0.6.5+commit.f956cc89

Optimization Enabled:
Yes with 2000 runs

Other Settings:
default evmVersion, MIT license
File 1 of 18 : CatalystMinter.sol
pragma solidity 0.6.5;
pragma experimental ABIEncoderV2;

import "./Interfaces/AssetToken.sol";
import "./common/Interfaces/ERC20.sol";
import "./Interfaces/ERC20Extended.sol";
import "./common/BaseWithStorage/MetaTransactionReceiver.sol";
import "./common/Libraries/SafeMathWithRequire.sol";
import "./Catalyst/GemToken.sol";
import "./Catalyst/CatalystToken.sol";
import "./CatalystRegistry.sol";
import "./BaseWithStorage/ERC20Group.sol";


/// @notice Gateway to mint Asset with Catalyst, Gems and Sand
contract CatalystMinter is MetaTransactionReceiver {
    /// @dev emitted when fee collector (that receive the sand fee) get changed
    /// @param newCollector address of the new collector, address(0) means the fee will be burned
    event FeeCollector(address newCollector);

    function setFeeCollector(address newCollector) external {
        require(msg.sender == _admin, "NOT_AUTHORIZED_ADMIN");
        _setFeeCollector(newCollector);
    }

    event GemAdditionFee(uint256 newFee);

    function setGemAdditionFee(uint256 newFee) external {
        require(msg.sender == _admin, "NOT_AUTHORIZED_ADMIN");
        _setGemAdditionFee(newFee);
    }

    /// @notice mint one Asset token.
    /// @param from address creating the Asset, need to be the tx sender or meta tx signer.
    /// @param packId unused packId that will let you predict the resulting tokenId.
    /// @param metadataHash cidv1 ipfs hash of the folder where 0.json file contains the metadata.
    /// @param catalystId address of the Catalyst ERC20 token to burn.
    /// @param gemIds list of gem ids to burn in the catalyst.
    /// @param quantity asset supply to mint
    /// @param to destination address receiving the minted tokens.
    /// @param data extra data.
    function mint(
        address from,
        uint40 packId,
        bytes32 metadataHash,
        uint256 catalystId,
        uint256[] calldata gemIds,
        uint256 quantity,
        address to,
        bytes calldata data
    ) external returns (uint256) {
        _checkAuthorization(from, to);
        _burnCatalyst(from, catalystId);
        uint16 maxGems = _checkQuantityAndBurnSandAndGems(from, catalystId, gemIds, quantity);
        uint256 id = _asset.mint(from, packId, metadataHash, quantity, 0, to, data);
        _catalystRegistry.setCatalyst(id, catalystId, maxGems, gemIds);
        return id;
    }

    /// @notice associate a catalyst to a fungible Asset token by extracting it as ERC721 first.
    /// @param from address from which the Asset token belongs to.
    /// @param assetId tokenId of the Asset being extracted.
    /// @param catalystId address of the catalyst token to use and burn.
    /// @param gemIds list of gems to socket into the catalyst (burned).
    /// @param to destination address receiving the extracted and upgraded ERC721 Asset token.
    function extractAndChangeCatalyst(
        address from,
        uint256 assetId,
        uint256 catalystId,
        uint256[] calldata gemIds,
        address to
    ) external returns (uint256 tokenId) {
        _checkAuthorization(from, to);
        tokenId = _asset.extractERC721From(from, assetId, from);
        _changeCatalyst(from, tokenId, catalystId, gemIds, to);
    }

    /// @notice associate a new catalyst to a non-fungible Asset token.
    /// @param from address from which the Asset token belongs to.
    /// @param assetId tokenId of the Asset being updated.
    /// @param catalystId address of the catalyst token to use and burn.
    /// @param gemIds list of gems to socket into the catalyst (burned).
    /// @param to destination address receiving the Asset token.
    function changeCatalyst(
        address from,
        uint256 assetId,
        uint256 catalystId,
        uint256[] calldata gemIds,
        address to
    ) external returns (uint256 tokenId) {
        _checkAuthorization(from, to);
        _changeCatalyst(from, assetId, catalystId, gemIds, to);
        return assetId;
    }

    /// @notice add gems to a fungible Asset token by extracting it as ERC721 first.
    /// @param from address from which the Asset token belongs to.
    /// @param assetId tokenId of the Asset being extracted.
    /// @param gemIds list of gems to socket into the existing catalyst (burned).
    /// @param to destination address receiving the extracted and upgraded ERC721 Asset token.
    function extractAndAddGems(
        address from,
        uint256 assetId,
        uint256[] calldata gemIds,
        address to
    ) external returns (uint256 tokenId) {
        _checkAuthorization(from, to);
        tokenId = _asset.extractERC721From(from, assetId, from);
        _addGems(from, tokenId, gemIds, to);
    }

    /// @notice add gems to a non-fungible Asset token.
    /// @param from address from which the Asset token belongs to.
    /// @param assetId tokenId of the Asset to which the gems will be added to.
    /// @param gemIds list of gems to socket into the existing catalyst (burned).
    /// @param to destination address receiving the extracted and upgraded ERC721 Asset token.
    function addGems(
        address from,
        uint256 assetId,
        uint256[] calldata gemIds,
        address to
    ) external {
        _checkAuthorization(from, to);
        _addGems(from, assetId, gemIds, to);
    }

    struct AssetData {
        uint256[] gemIds;
        uint256 quantity;
        uint256 catalystId;
    }

    /// @notice mint multiple Asset tokens.
    /// @param from address creating the Asset, need to be the tx sender or meta tx signer.
    /// @param packId unused packId that will let you predict the resulting tokenId.
    /// @param metadataHash cidv1 ipfs hash of the folder where 0.json file contains the metadata.
    /// @param gemsQuantities quantities of gems to be used for each id in order
    /// @param catalystsQuantities quantities of catalyst to be used for each id in order
    /// @param assets contains the data to associate catalyst and gems to the assets.
    /// @param to destination address receiving the minted tokens.
    /// @param data extra data.
    function mintMultiple(
        address from,
        uint40 packId,
        bytes32 metadataHash,
        uint256[] memory gemsQuantities,
        uint256[] memory catalystsQuantities,
        AssetData[] memory assets,
        address to,
        bytes memory data
    ) public returns (uint256[] memory ids) {
        require(assets.length != 0, "INVALID_0_ASSETS");
        _checkAuthorization(from, to);
        return _mintMultiple(from, packId, metadataHash, gemsQuantities, catalystsQuantities, assets, to, data);
    }

    // //////////////////// INTERNALS ////////////////////

    function _checkQuantityAndBurnSandAndGems(
        address from,
        uint256 catalystId,
        uint256[] memory gemIds,
        uint256 quantity
    ) internal returns (uint16) {
        (uint16 maxGems, uint16 minQuantity, uint16 maxQuantity, uint256 sandMintingFee, ) = _getMintData(catalystId);
        require(minQuantity <= quantity && quantity <= maxQuantity, "INVALID_QUANTITY");
        require(gemIds.length <= maxGems, "INVALID_GEMS_TOO_MANY");
        _burnSingleGems(from, gemIds);
        _chargeSand(from, quantity.mul(sandMintingFee));
        return maxGems;
    }

    function _mintMultiple(
        address from,
        uint40 packId,
        bytes32 metadataHash,
        uint256[] memory gemsQuantities,
        uint256[] memory catalystsQuantities,
        AssetData[] memory assets,
        address to,
        bytes memory data
    ) internal returns (uint256[] memory) {
        (uint256 totalSandFee, uint256[] memory supplies, uint16[] memory maxGemsList) = _handleMultipleCatalysts(
            from,
            gemsQuantities,
            catalystsQuantities,
            assets
        );

        _chargeSand(from, totalSandFee);

        return _mintAssets(from, packId, metadataHash, assets, supplies, maxGemsList, to, data);
    }

    function _chargeSand(address from, uint256 sandFee) internal {
        address feeCollector = _feeCollector;
        if (feeCollector != address(0) && sandFee != 0) {
            if (feeCollector == address(BURN_ADDRESS)) {
                // special address for burn
                _sand.burnFor(from, sandFee);
            } else {
                _sand.transferFrom(from, _feeCollector, sandFee);
            }
        }
    }

    function _extractMintData(uint256 data)
        internal
        pure
        returns (
            uint16 maxGems,
            uint16 minQuantity,
            uint16 maxQuantity,
            uint256 sandMintingFee,
            uint256 sandUpdateFee
        )
    {
        maxGems = uint16(data >> 240);
        minQuantity = uint16((data >> 224) % 2**16);
        maxQuantity = uint16((data >> 208) % 2**16);
        sandMintingFee = uint256((data >> 120) % 2**88);
        sandUpdateFee = uint256(data % 2**88);
    }

    function _getMintData(uint256 catalystId)
        internal
        view
        returns (
            uint16,
            uint16,
            uint16,
            uint256,
            uint256
        )
    {
        if (catalystId == 0) {
            return _extractMintData(_common_mint_data);
        } else if (catalystId == 1) {
            return _extractMintData(_rare_mint_data);
        } else if (catalystId == 2) {
            return _extractMintData(_epic_mint_data);
        } else if (catalystId == 3) {
            return _extractMintData(_legendary_mint_data);
        }
        return _catalysts.getMintData(catalystId);
    }

    function _handleMultipleCatalysts(
        address from,
        uint256[] memory gemsQuantities,
        uint256[] memory catalystsQuantities,
        AssetData[] memory assets
    )
        internal
        returns (
            uint256 totalSandFee,
            uint256[] memory supplies,
            uint16[] memory maxGemsList
        )
    {
        _burnCatalysts(from, catalystsQuantities);
        _burnGems(from, gemsQuantities);

        supplies = new uint256[](assets.length);
        maxGemsList = new uint16[](assets.length);

        for (uint256 i = 0; i < assets.length; i++) {
            require(catalystsQuantities[assets[i].catalystId] != 0, "INVALID_CATALYST_NOT_ENOUGH");
            catalystsQuantities[assets[i].catalystId]--;
            gemsQuantities = _checkGemsQuantities(gemsQuantities, assets[i].gemIds);
            (uint16 maxGems, uint16 minQuantity, uint16 maxQuantity, uint256 sandMintingFee, ) = _getMintData(assets[i].catalystId);
            require(minQuantity <= assets[i].quantity && assets[i].quantity <= maxQuantity, "INVALID_QUANTITY");
            require(assets[i].gemIds.length <= maxGems, "INVALID_GEMS_TOO_MANY");
            maxGemsList[i] = maxGems;
            supplies[i] = assets[i].quantity;
            totalSandFee = totalSandFee.add(sandMintingFee.mul(assets[i].quantity));
        }
    }

    function _checkGemsQuantities(uint256[] memory gemsQuantities, uint256[] memory gemIds) internal pure returns (uint256[] memory) {
        for (uint256 i = 0; i < gemIds.length; i++) {
            require(gemsQuantities[gemIds[i]] != 0, "INVALID_GEMS_NOT_ENOUGH");
            gemsQuantities[gemIds[i]]--;
        }
        return gemsQuantities;
    }

    function _burnCatalysts(address from, uint256[] memory catalystsQuantities) internal {
        uint256[] memory ids = new uint256[](catalystsQuantities.length);
        for (uint256 i = 0; i < ids.length; i++) {
            ids[i] = i;
        }
        _catalysts.batchBurnFrom(from, ids, catalystsQuantities);
    }

    function _burnGems(address from, uint256[] memory gemsQuantities) internal {
        uint256[] memory ids = new uint256[](gemsQuantities.length);
        for (uint256 i = 0; i < ids.length; i++) {
            ids[i] = i;
        }
        _gems.batchBurnFrom(from, ids, gemsQuantities);
    }

    function _mintAssets(
        address from,
        uint40 packId,
        bytes32 metadataHash,
        AssetData[] memory assets,
        uint256[] memory supplies,
        uint16[] memory maxGemsList,
        address to,
        bytes memory data
    ) internal returns (uint256[] memory tokenIds) {
        tokenIds = _asset.mintMultiple(from, packId, metadataHash, supplies, "", to, data);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            _catalystRegistry.setCatalyst(tokenIds[i], assets[i].catalystId, maxGemsList[i], assets[i].gemIds);
        }
    }

    function _changeCatalyst(
        address from,
        uint256 assetId,
        uint256 catalystId,
        uint256[] memory gemIds,
        address to
    ) internal {
        require(assetId & IS_NFT != 0, "INVALID_NOT_NFT"); // Asset (ERC1155ERC721.sol) ensure NFT will return true here and non-NFT will return false
        _burnCatalyst(from, catalystId);
        (uint16 maxGems, , , , uint256 sandUpdateFee) = _getMintData(catalystId);
        require(gemIds.length <= maxGems, "INVALID_GEMS_TOO_MANY");
        _burnGems(from, gemIds);
        _chargeSand(from, sandUpdateFee);

        _catalystRegistry.setCatalyst(assetId, catalystId, maxGems, gemIds);

        _transfer(from, to, assetId);
    }

    function _addGems(
        address from,
        uint256 assetId,
        uint256[] memory gemIds,
        address to
    ) internal {
        require(assetId & IS_NFT != 0, "INVALID_NOT_NFT"); // Asset (ERC1155ERC721.sol) ensure NFT will return true here and non-NFT will return false
        _catalystRegistry.addGems(assetId, gemIds);
        _chargeSand(from, gemIds.length.mul(_gemAdditionFee));
        _transfer(from, to, assetId);
    }

    function _transfer(
        address from,
        address to,
        uint256 assetId
    ) internal {
        if (from != to) {
            _asset.safeTransferFrom(from, to, assetId);
        }
    }

    function _checkAuthorization(address from, address to) internal view {
        require(to != address(0), "INVALID_TO_ZERO_ADDRESS");
        require(from == msg.sender || _metaTransactionContracts[msg.sender], "NOT_SENDER");
    }

    function _burnSingleGems(address from, uint256[] memory gemIds) internal {
        uint256[] memory amounts = new uint256[](gemIds.length);
        for (uint256 i = 0; i < gemIds.length; i++) {
            amounts[i] = 1;
        }
        _gems.batchBurnFrom(from, gemIds, amounts);
    }

    function _burnCatalyst(address from, uint256 catalystId) internal {
        _catalysts.burnFrom(from, catalystId, 1);
    }

    function _setFeeCollector(address newCollector) internal {
        _feeCollector = newCollector;
        emit FeeCollector(newCollector);
    }

    function _setGemAdditionFee(uint256 newFee) internal {
        _gemAdditionFee = newFee;
        emit GemAdditionFee(newFee);
    }

    // /////////////////// UTILITIES /////////////////////
    using SafeMathWithRequire for uint256;

    // //////////////////////// DATA /////////////////////
    uint256 private constant IS_NFT = 0x0000000000000000000000000000000000000000800000000000000000000000;
    address private constant BURN_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;

    ERC20Extended internal immutable _sand;
    AssetToken internal immutable _asset;
    GemToken internal immutable _gems;
    CatalystToken internal immutable _catalysts;
    CatalystRegistry internal immutable _catalystRegistry;
    address internal _feeCollector;

    uint256 internal immutable _common_mint_data;
    uint256 internal immutable _rare_mint_data;
    uint256 internal immutable _epic_mint_data;
    uint256 internal immutable _legendary_mint_data;

    uint256 internal _gemAdditionFee;

    // /////////////////// CONSTRUCTOR ////////////////////
    constructor(
        CatalystRegistry catalystRegistry,
        ERC20Extended sand,
        AssetToken asset,
        GemToken gems,
        address metaTx,
        address admin,
        address feeCollector,
        uint256 gemAdditionFee,
        CatalystToken catalysts,
        uint256[4] memory bakedInMintdata
    ) public {
        _catalystRegistry = catalystRegistry;
        _sand = sand;
        _asset = asset;
        _gems = gems;
        _catalysts = catalysts;
        _admin = admin;
        _setGemAdditionFee(gemAdditionFee);
        _setFeeCollector(feeCollector);
        _setMetaTransactionProcessor(metaTx, true);
        _common_mint_data = bakedInMintdata[0];
        _rare_mint_data = bakedInMintdata[1];
        _epic_mint_data = bakedInMintdata[2];
        _legendary_mint_data = bakedInMintdata[3];
    }
}

File 2 of 18 : ERC20Group.sol
pragma solidity 0.6.5;
pragma experimental ABIEncoderV2;

import "./ERC20SubToken.sol";
import "../common/Libraries/SafeMath.sol";
import "../common/Libraries/AddressUtils.sol";
import "../common/Libraries/ObjectLib32.sol";
import "../common/Libraries/BytesUtil.sol";

import "../common/BaseWithStorage/SuperOperators.sol";
import "../common/BaseWithStorage/MetaTransactionReceiver.sol";


contract ERC20Group is SuperOperators, MetaTransactionReceiver {
    uint256 internal constant MAX_UINT256 = ~uint256(0);

    /// @notice emitted when a new Token is added to the group.
    /// @param subToken the token added, its id will be its index in the array.
    event SubToken(ERC20SubToken subToken);

    /// @notice emitted when `owner` is allowing or disallowing `operator` to transfer tokens on its behalf.
    /// @param owner the address approving.
    /// @param operator the address being granted (or revoked) permission to transfer.
    /// @param approved whether the operator is granted transfer right or not.
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    event Minter(address minter, bool enabled);

    /// @notice Enable or disable the ability of `minter` to mint tokens
    /// @param minter address that will be given/removed minter right.
    /// @param enabled set whether the minter is enabled or disabled.
    function setMinter(address minter, bool enabled) external {
        require(msg.sender == _admin, "NOT_AUTHORIZED_ADMIN");
        _setMinter(minter, enabled);
    }

    /// @notice check whether address `who` is given minter rights.
    /// @param who The address to query.
    /// @return whether the address has minter rights.
    function isMinter(address who) public view returns (bool) {
        return _minters[who];
    }

    /// @dev mint more tokens of a specific subToken .
    /// @param to address receiving the tokens.
    /// @param id subToken id (also the index at which it was added).
    /// @param amount of token minted.
    function mint(
        address to,
        uint256 id,
        uint256 amount
    ) external {
        require(_minters[msg.sender], "NOT_AUTHORIZED_MINTER");
        (uint256 bin, uint256 index) = id.getTokenBinIndex();
        mapping(uint256 => uint256) storage toPack = _packedTokenBalance[to];
        toPack[bin] = toPack[bin].updateTokenBalance(index, amount, ObjectLib32.Operations.ADD);
        _packedSupplies[bin] = _packedSupplies[bin].updateTokenBalance(index, amount, ObjectLib32.Operations.ADD);
        _erc20s[id].emitTransferEvent(address(0), to, amount);
    }

    /// @dev mint more tokens of a several subToken .
    /// @param to address receiving the tokens.
    /// @param ids subToken ids (also the index at which it was added).
    /// @param amounts for each token minted.
    function batchMint(
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts
    ) external {
        require(_minters[msg.sender], "NOT_AUTHORIZED_MINTER");
        require(ids.length == amounts.length, "INVALID_INCONSISTENT_LENGTH");
        _batchMint(to, ids, amounts);
    }

    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal {
        uint256 lastBin = MAX_UINT256;
        uint256 bal = 0;
        uint256 supply = 0;
        mapping(uint256 => uint256) storage toPack = _packedTokenBalance[to];
        for (uint256 i = 0; i < ids.length; i++) {
            if (amounts[i] != 0) {
                (uint256 bin, uint256 index) = ids[i].getTokenBinIndex();
                if (lastBin == MAX_UINT256) {
                    lastBin = bin;
                    bal = toPack[bin].updateTokenBalance(index, amounts[i], ObjectLib32.Operations.ADD);
                    supply = _packedSupplies[bin].updateTokenBalance(index, amounts[i], ObjectLib32.Operations.ADD);
                } else {
                    if (bin != lastBin) {
                        toPack[lastBin] = bal;
                        bal = toPack[bin];
                        _packedSupplies[lastBin] = supply;
                        supply = _packedSupplies[bin];
                        lastBin = bin;
                    }
                    bal = bal.updateTokenBalance(index, amounts[i], ObjectLib32.Operations.ADD);
                    supply = supply.updateTokenBalance(index, amounts[i], ObjectLib32.Operations.ADD);
                }
                _erc20s[ids[i]].emitTransferEvent(address(0), to, amounts[i]);
            }
        }
        if (lastBin != MAX_UINT256) {
            toPack[lastBin] = bal;
            _packedSupplies[lastBin] = supply;
        }
    }

    /// @notice return the current total supply of a specific subToken.
    /// @param id subToken id.
    /// @return supply current total number of tokens.
    function supplyOf(uint256 id) external view returns (uint256 supply) {
        (uint256 bin, uint256 index) = id.getTokenBinIndex();
        return _packedSupplies[bin].getValueInBin(index);
    }

    /// @notice return the balance of a particular owner for a particular subToken.
    /// @param owner whose balance it is of.
    /// @param id subToken id.
    /// @return balance of the owner
    function balanceOf(address owner, uint256 id) public view returns (uint256 balance) {
        (uint256 bin, uint256 index) = id.getTokenBinIndex();
        return _packedTokenBalance[owner][bin].getValueInBin(index);
    }

    /// @notice return the balances of a list of owners / subTokens.
    /// @param owners list of addresses to which we want to know the balance.
    /// @param ids list of subTokens's addresses.
    /// @return balances list of balances for each request.
    function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory balances) {
        require(owners.length == ids.length, "INVALID_INCONSISTENT_LENGTH");
        balances = new uint256[](ids.length);
        for (uint256 i = 0; i < ids.length; i++) {
            balances[i] = balanceOf(owners[i], ids[i]);
        }
    }

    /// @notice transfer a number of subToken from one address to another.
    /// @param from owner to transfer from.
    /// @param to destination address that will receive the tokens.
    /// @param id subToken id.
    /// @param value amount of tokens to transfer.
    function singleTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 value
    ) external {
        require(to != address(0), "INVALID_TO_ZERO_ADDRESS");
        ERC20SubToken erc20 = _erc20s[id];
        require(
            from == msg.sender ||
                msg.sender == address(erc20) ||
                _metaTransactionContracts[msg.sender] ||
                _superOperators[msg.sender] ||
                _operatorsForAll[from][msg.sender],
            "NOT_AUTHORIZED"
        );

        (uint256 bin, uint256 index) = id.getTokenBinIndex();
        mapping(uint256 => uint256) storage fromPack = _packedTokenBalance[from];
        mapping(uint256 => uint256) storage toPack = _packedTokenBalance[to];
        fromPack[bin] = fromPack[bin].updateTokenBalance(index, value, ObjectLib32.Operations.SUB);
        toPack[bin] = toPack[bin].updateTokenBalance(index, value, ObjectLib32.Operations.ADD);
        erc20.emitTransferEvent(from, to, value);
    }

    /// @notice transfer a number of different subTokens from one address to another.
    /// @param from owner to transfer from.
    /// @param to destination address that will receive the tokens.
    /// @param ids list of subToken ids to transfer.
    /// @param values list of amount for eacg subTokens to transfer.
    function batchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata values
    ) external {
        require(ids.length == values.length, "INVALID_INCONSISTENT_LENGTH");
        require(to != address(0), "INVALID_TO_ZERO_ADDRESS");
        require(
            from == msg.sender || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender] || _metaTransactionContracts[msg.sender],
            "NOT_AUTHORIZED"
        );
        _batchTransferFrom(from, to, ids, values);
    }

    function _batchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory values
    ) internal {
        uint256 lastBin = MAX_UINT256;
        uint256 balFrom;
        uint256 balTo;
        mapping(uint256 => uint256) storage fromPack = _packedTokenBalance[from];
        mapping(uint256 => uint256) storage toPack = _packedTokenBalance[to];
        for (uint256 i = 0; i < ids.length; i++) {
            if (values[i] != 0) {
                (uint256 bin, uint256 index) = ids[i].getTokenBinIndex();
                if (lastBin == MAX_UINT256) {
                    lastBin = bin;
                    balFrom = ObjectLib32.updateTokenBalance(fromPack[bin], index, values[i], ObjectLib32.Operations.SUB);
                    balTo = ObjectLib32.updateTokenBalance(toPack[bin], index, values[i], ObjectLib32.Operations.ADD);
                } else {
                    if (bin != lastBin) {
                        fromPack[lastBin] = balFrom;
                        toPack[lastBin] = balTo;
                        balFrom = fromPack[bin];
                        balTo = toPack[bin];
                        lastBin = bin;
                    }
                    balFrom = balFrom.updateTokenBalance(index, values[i], ObjectLib32.Operations.SUB);
                    balTo = balTo.updateTokenBalance(index, values[i], ObjectLib32.Operations.ADD);
                }
                ERC20SubToken erc20 = _erc20s[ids[i]];
                erc20.emitTransferEvent(from, to, values[i]);
            }
        }
        if (lastBin != MAX_UINT256) {
            fromPack[lastBin] = balFrom;
            toPack[lastBin] = balTo;
        }
    }

    /// @notice grant or revoke the ability for an address to transfer token on behalf of another address.
    /// @param sender address granting/revoking the approval.
    /// @param operator address being granted/revoked ability to transfer.
    /// @param approved whether the operator is revoked or approved.
    function setApprovalForAllFor(
        address sender,
        address operator,
        bool approved
    ) external {
        require(msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender], "NOT_AUTHORIZED");
        _setApprovalForAll(sender, operator, approved);
    }

    /// @notice grant or revoke the ability for an address to transfer token on your behalf.
    /// @param operator address being granted/revoked ability to transfer.
    /// @param approved whether the operator is revoked or approved.
    function setApprovalForAll(address operator, bool approved) external {
        _setApprovalForAll(msg.sender, operator, approved);
    }

    /// @notice return whether an oeprator has the ability to transfer on behalf of another address.
    /// @param owner address who would have granted the rights.
    /// @param operator address being given the ability to transfer.
    /// @return isOperator whether the operator has approval rigths or not.
    function isApprovedForAll(address owner, address operator) external view returns (bool isOperator) {
        return _operatorsForAll[owner][operator] || _superOperators[operator];
    }

    function isAuthorizedToTransfer(address owner, address sender) external view returns (bool) {
        return _metaTransactionContracts[sender] || _superOperators[sender] || _operatorsForAll[owner][sender];
    }

    function isAuthorizedToApprove(address sender) external view returns (bool) {
        return _metaTransactionContracts[sender] || _superOperators[sender];
    }

    function batchBurnFrom(
        address from,
        uint256[] calldata ids,
        uint256[] calldata amounts
    ) external {
        require(from != address(0), "INVALID_FROM_ZERO_ADDRESS");
        require(
            from == msg.sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender],
            "NOT_AUTHORIZED"
        );

        _batchBurnFrom(from, ids, amounts);
    }

    /// @notice burn token for a specific owner and subToken.
    /// @param from fron which address the token are burned from.
    /// @param id subToken id.
    /// @param value amount of tokens to burn.
    function burnFrom(
        address from,
        uint256 id,
        uint256 value
    ) external {
        require(
            from == msg.sender || _superOperators[msg.sender] || _operatorsForAll[from][msg.sender] || _metaTransactionContracts[msg.sender],
            "NOT_AUTHORIZED"
        );
        _burn(from, id, value);
    }

    /// @notice burn token for a specific subToken.
    /// @param id subToken id.
    /// @param value amount of tokens to burn.
    function burn(uint256 id, uint256 value) external {
        _burn(msg.sender, id, value);
    }

    // ///////////////// INTERNAL //////////////////////////

    function _batchBurnFrom(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal {
        uint256 balFrom = 0;
        uint256 supply = 0;
        uint256 lastBin = MAX_UINT256;
        mapping(uint256 => uint256) storage fromPack = _packedTokenBalance[from];
        for (uint256 i = 0; i < ids.length; i++) {
            if (amounts[i] != 0) {
                (uint256 bin, uint256 index) = ids[i].getTokenBinIndex();
                if (lastBin == MAX_UINT256) {
                    lastBin = bin;
                    balFrom = fromPack[bin].updateTokenBalance(index, amounts[i], ObjectLib32.Operations.SUB);
                    supply = _packedSupplies[bin].updateTokenBalance(index, amounts[i], ObjectLib32.Operations.SUB);
                } else {
                    if (bin != lastBin) {
                        fromPack[lastBin] = balFrom;
                        balFrom = fromPack[bin];
                        _packedSupplies[lastBin] = supply;
                        supply = _packedSupplies[bin];
                        lastBin = bin;
                    }

                    balFrom = balFrom.updateTokenBalance(index, amounts[i], ObjectLib32.Operations.SUB);
                    supply = supply.updateTokenBalance(index, amounts[i], ObjectLib32.Operations.SUB);
                }
                _erc20s[ids[i]].emitTransferEvent(from, address(0), amounts[i]);
            }
        }
        if (lastBin != MAX_UINT256) {
            fromPack[lastBin] = balFrom;
            _packedSupplies[lastBin] = supply;
        }
    }

    function _burn(
        address from,
        uint256 id,
        uint256 value
    ) internal {
        ERC20SubToken erc20 = _erc20s[id];
        (uint256 bin, uint256 index) = id.getTokenBinIndex();
        mapping(uint256 => uint256) storage fromPack = _packedTokenBalance[from];
        fromPack[bin] = ObjectLib32.updateTokenBalance(fromPack[bin], index, value, ObjectLib32.Operations.SUB);
        _packedSupplies[bin] = ObjectLib32.updateTokenBalance(_packedSupplies[bin], index, value, ObjectLib32.Operations.SUB);
        erc20.emitTransferEvent(from, address(0), value);
    }

    function _addSubToken(ERC20SubToken subToken) internal returns (uint256 id) {
        id = _erc20s.length;
        require(subToken.groupAddress() == address(this), "INVALID_GROUP");
        require(subToken.groupTokenId() == id, "INVALID_ID");
        _erc20s.push(subToken);
        emit SubToken(subToken);
    }

    function _setApprovalForAll(
        address sender,
        address operator,
        bool approved
    ) internal {
        require(!_superOperators[operator], "INVALID_SUPER_OPERATOR");
        _operatorsForAll[sender][operator] = approved;
        emit ApprovalForAll(sender, operator, approved);
    }

    function _setMinter(address minter, bool enabled) internal {
        _minters[minter] = enabled;
        emit Minter(minter, enabled);
    }

    // ///////////////// UTILITIES /////////////////////////
    using AddressUtils for address;
    using ObjectLib32 for ObjectLib32.Operations;
    using ObjectLib32 for uint256;
    using SafeMath for uint256;

    // ////////////////// DATA ///////////////////////////////
    mapping(uint256 => uint256) internal _packedSupplies;
    mapping(address => mapping(uint256 => uint256)) internal _packedTokenBalance;
    mapping(address => mapping(address => bool)) internal _operatorsForAll;
    ERC20SubToken[] internal _erc20s;
    mapping(address => bool) internal _minters;

    // ////////////// CONSTRUCTOR ////////////////////////////

    struct SubTokenData {
        string name;
        string symbol;
    }

    constructor(
        address metaTransactionContract,
        address admin,
        address initialMinter
    ) internal {
        _admin = admin;
        _setMetaTransactionProcessor(metaTransactionContract, true);
        _setMinter(initialMinter, true);
    }
}

File 3 of 18 : ERC20SubToken.sol
pragma solidity 0.6.5;

import "../common/Libraries/SafeMathWithRequire.sol";
import "../common/BaseWithStorage/SuperOperators.sol";
import "../common/BaseWithStorage/MetaTransactionReceiver.sol";

import "./ERC20Group.sol";


contract ERC20SubToken {
    // TODO add natspec, currently blocked by solidity compiler issue
    event Transfer(address indexed from, address indexed to, uint256 value);

    // TODO add natspec, currently blocked by solidity compiler issue
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice A descriptive name for the tokens
    /// @return name of the tokens
    function name() public view returns (string memory) {
        return _name;
    }

    /// @notice An abbreviated name for the tokens
    /// @return symbol of the tokens
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /// @notice the tokenId in ERC20Group
    /// @return the tokenId in ERC20Group
    function groupTokenId() external view returns (uint256) {
        return _index;
    }

    /// @notice the ERC20Group address
    /// @return the address of the group
    function groupAddress() external view returns (address) {
        return address(_group);
    }

    function totalSupply() external view returns (uint256) {
        return _group.supplyOf(_index);
    }

    function balanceOf(address who) external view returns (uint256) {
        return _group.balanceOf(who, _index);
    }

    function decimals() external pure returns (uint8) {
        return uint8(0);
    }

    function transfer(address to, uint256 amount) external returns (bool success) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool success) {
        if (msg.sender != from && !_group.isAuthorizedToTransfer(from, msg.sender)) {
            uint256 allowance = _mAllowed[from][msg.sender];
            if (allowance != ~uint256(0)) {
                // save gas when allowance is maximal by not reducing it (see https://github.com/ethereum/EIPs/issues/717)
                require(allowance >= amount, "NOT_AUTHOIZED_ALLOWANCE");
                _mAllowed[from][msg.sender] = allowance - amount;
            }
        }
        _transfer(from, to, amount);
        return true;
    }

    function approve(address spender, uint256 amount) external returns (bool success) {
        _approveFor(msg.sender, spender, amount);
        return true;
    }

    function approveFor(
        address from,
        address spender,
        uint256 amount
    ) external returns (bool success) {
        require(msg.sender == from || _group.isAuthorizedToApprove(msg.sender), "NOT_AUTHORIZED");
        _approveFor(from, spender, amount);
        return true;
    }

    function emitTransferEvent(
        address from,
        address to,
        uint256 amount
    ) external {
        require(msg.sender == address(_group), "NOT_AUTHORIZED_GROUP_ONLY");
        emit Transfer(from, to, amount);
    }

    // /////////////////// INTERNAL ////////////////////////

    function _approveFor(
        address owner,
        address spender,
        uint256 amount
    ) internal {
        require(owner != address(0) && spender != address(0), "INVALID_FROM_OR_SPENDER");
        _mAllowed[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    function allowance(address owner, address spender) external view returns (uint256 remaining) {
        return _mAllowed[owner][spender];
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal {
        _group.singleTransferFrom(from, to, _index, amount);
    }

    // ///////////////////// UTILITIES ///////////////////////
    using SafeMathWithRequire for uint256;

    // //////////////////// CONSTRUCTOR /////////////////////
    constructor(
        ERC20Group group,
        uint256 index,
        string memory tokenName,
        string memory tokenSymbol
    ) public {
        _group = group;
        _index = index;
        _name = tokenName;
        _symbol = tokenSymbol;
    }

    // ////////////////////// DATA ///////////////////////////
    ERC20Group internal immutable _group;
    uint256 internal immutable _index;
    mapping(address => mapping(address => uint256)) internal _mAllowed;
    string internal _name;
    string internal _symbol;
}

File 4 of 18 : CatalystToken.sol
pragma solidity 0.6.5;
pragma experimental ABIEncoderV2;


interface CatalystToken {
    function getMintData(uint256 catalystId)
        external
        view
        returns (
            uint16 maxGems,
            uint16 minQuantity,
            uint16 maxQuantity,
            uint256 sandMintingFee,
            uint256 sandUpdateFee
        );

    function batchBurnFrom(
        address from,
        uint256[] calldata ids,
        uint256[] calldata amounts
    ) external;

    function burnFrom(
        address from,
        uint256 id,
        uint256 amount
    ) external;
}

File 5 of 18 : CatalystValue.sol
pragma solidity 0.6.5;
pragma experimental ABIEncoderV2;


interface CatalystValue {
    struct GemEvent {
        uint256[] gemIds;
        bytes32 blockHash;
    }

    function getValues(
        uint256 catalystId,
        uint256 seed,
        GemEvent[] calldata events,
        uint32 totalNumberOfGemTypes
    ) external view returns (uint32[] memory values);
}

File 6 of 18 : GemToken.sol
pragma solidity 0.6.5;


interface GemToken {
    function batchBurnFrom(
        address from,
        uint256[] calldata ids,
        uint256[] calldata amounts
    ) external;
}

File 7 of 18 : CatalystRegistry.sol
pragma solidity 0.6.5;
pragma experimental ABIEncoderV2;

import "./Interfaces/AssetToken.sol";
import "./common/BaseWithStorage/Admin.sol";
import "./Catalyst/CatalystValue.sol";


contract CatalystRegistry is Admin, CatalystValue {
    event Minter(address indexed newMinter);
    event CatalystApplied(uint256 indexed assetId, uint256 indexed catalystId, uint256 seed, uint256[] gemIds, uint64 blockNumber);
    event GemsAdded(uint256 indexed assetId, uint256 seed, uint256[] gemIds, uint64 blockNumber);

    function getCatalyst(uint256 assetId) external view returns (bool exists, uint256 catalystId) {
        CatalystStored memory catalyst = _catalysts[assetId];
        if (catalyst.set != 0) {
            return (true, catalyst.catalystId);
        }
        if (assetId & IS_NFT != 0) {
            catalyst = _catalysts[_getCollectionId(assetId)];
            return (catalyst.set != 0, catalyst.catalystId);
        }
        return (false, 0);
    }

    function setCatalyst(
        uint256 assetId,
        uint256 catalystId,
        uint256 maxGems,
        uint256[] calldata gemIds
    ) external {
        require(msg.sender == _minter, "NOT_AUTHORIZED_MINTER");
        require(gemIds.length <= maxGems, "INVALID_GEMS_TOO_MANY");
        uint256 emptySockets = maxGems - gemIds.length;
        _catalysts[assetId] = CatalystStored(uint64(emptySockets), uint64(catalystId), 1);
        uint64 blockNumber = _getBlockNumber();
        emit CatalystApplied(assetId, catalystId, assetId, gemIds, blockNumber);
    }

    function addGems(uint256 assetId, uint256[] calldata gemIds) external {
        require(msg.sender == _minter, "NOT_AUTHORIZED_MINTER");
        require(assetId & IS_NFT != 0, "INVALID_NOT_NFT");
        require(gemIds.length != 0, "INVALID_GEMS_0");
        (uint256 emptySockets, uint256 seed) = _getSocketData(assetId);
        require(emptySockets >= gemIds.length, "INVALID_GEMS_TOO_MANY");
        emptySockets -= gemIds.length;
        _catalysts[assetId].emptySockets = uint64(emptySockets);
        uint64 blockNumber = _getBlockNumber();
        emit GemsAdded(assetId, seed, gemIds, blockNumber);
    }

    /// @dev Set the Minter that will be the only address able to create Estate
    /// @param minter address of the minter
    function setMinter(address minter) external {
        require(msg.sender == _admin, "NOT_AUTHORIZED_ADMIN");
        require(minter != _minter, "INVALID_MINTER_SAME_ALREADY_SET");
        _minter = minter;
        emit Minter(minter);
    }

    /// @dev return the current minter
    function getMinter() external view returns (address) {
        return _minter;
    }

    function getValues(
        uint256 catalystId,
        uint256 seed,
        GemEvent[] calldata events,
        uint32 totalNumberOfGemTypes
    ) external override view returns (uint32[] memory values) {
        return _catalystValue.getValues(catalystId, seed, events, totalNumberOfGemTypes);
    }

    // ///////// INTERNAL ////////////

    uint256 private constant IS_NFT = 0x0000000000000000000000000000000000000000800000000000000000000000;
    uint256 private constant NOT_IS_NFT = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFF;
    uint256 private constant NOT_NFT_INDEX = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000007FFFFFFFFFFFFFFF;

    function _getSocketData(uint256 assetId) internal view returns (uint256 emptySockets, uint256 seed) {
        seed = assetId;
        CatalystStored memory catalyst = _catalysts[assetId];
        if (catalyst.set != 0) {
            // the gems are added to an asset who already get a specific catalyst.
            // the seed is its id
            return (catalyst.emptySockets, seed);
        }
        // else the asset is only adding gems while keeping the same seed (that of the original assetId)
        seed = _getCollectionId(assetId);
        catalyst = _catalysts[seed];
        return (catalyst.emptySockets, seed);
    }

    function _getBlockNumber() internal view returns (uint64 blockNumber) {
        blockNumber = uint64(block.number + 1);
    }

    function _getCollectionId(uint256 assetId) internal pure returns (uint256) {
        return assetId & NOT_NFT_INDEX & NOT_IS_NFT; // compute the same as Asset to get collectionId
    }

    // CONSTRUCTOR ////
    constructor(CatalystValue catalystValue, address admin) public {
        _admin = admin;
        _catalystValue = catalystValue;
    }

    /// DATA ////////

    struct CatalystStored {
        uint64 emptySockets;
        uint64 catalystId;
        uint64 set;
    }
    address internal _minter;
    CatalystValue internal immutable _catalystValue;
    mapping(uint256 => CatalystStored) internal _catalysts;
}

File 8 of 18 : AssetToken.sol
pragma solidity 0.6.5;


interface AssetToken {
    function mint(
        address creator,
        uint40 packId,
        bytes32 hash,
        uint256 supply,
        uint8 rarity,
        address owner,
        bytes calldata data
    ) external returns (uint256 id);

    function mintMultiple(
        address creator,
        uint40 packId,
        bytes32 hash,
        uint256[] calldata supplies,
        bytes calldata rarityPack,
        address owner,
        bytes calldata data
    ) external returns (uint256[] memory ids);

    // fails on non-NFT or nft who do not have collection (was a mistake)
    function collectionOf(uint256 id) external view returns (uint256);

    function balanceOf(address owner, uint256 id) external view returns (uint256);

    // return true for Non-NFT ERC1155 tokens which exists
    function isCollection(uint256 id) external view returns (bool);

    function collectionIndexOf(uint256 id) external view returns (uint256);

    function extractERC721From(
        address sender,
        uint256 id,
        address to
    ) external returns (uint256 newId);

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external;

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        bytes calldata data
    ) external;

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external;
}

File 9 of 18 : ERC20Extended.sol
pragma solidity 0.6.5;

import "../common/Interfaces/ERC20.sol";


interface ERC20Extended is ERC20 {
    function burnFor(address from, uint256 amount) external;

    function burn(uint256 amount) external;

    function approveFor(
        address owner,
        address spender,
        uint256 amount
    ) external returns (bool success);
}

File 10 of 18 : Admin.sol
pragma solidity 0.6.5;


contract Admin {
    address internal _admin;

    /// @dev emitted when the contract administrator is changed.
    /// @param oldAdmin address of the previous administrator.
    /// @param newAdmin address of the new administrator.
    event AdminChanged(address oldAdmin, address newAdmin);

    /// @dev gives the current administrator of this contract.
    /// @return the current administrator of this contract.
    function getAdmin() external view returns (address) {
        return _admin;
    }

    /// @dev change the administrator to be `newAdmin`.
    /// @param newAdmin address of the new administrator.
    function changeAdmin(address newAdmin) external {
        require(msg.sender == _admin, "only admin can change admin");
        emit AdminChanged(_admin, newAdmin);
        _admin = newAdmin;
    }

    modifier onlyAdmin() {
        require(msg.sender == _admin, "only admin allowed");
        _;
    }
}

File 11 of 18 : MetaTransactionReceiver.sol
pragma solidity 0.6.5;

import "./Admin.sol";


contract MetaTransactionReceiver is Admin {
    mapping(address => bool) internal _metaTransactionContracts;

    /// @dev emiited when a meta transaction processor is enabled/disabled
    /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights.
    /// @param enabled set whether the metaTransactionProcessor is enabled or disabled.
    event MetaTransactionProcessor(address metaTransactionProcessor, bool enabled);

    /// @dev Enable or disable the ability of `metaTransactionProcessor` to perform meta-tx (metaTransactionProcessor rights).
    /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights.
    /// @param enabled set whether the metaTransactionProcessor is enabled or disabled.
    function setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) public {
        require(msg.sender == _admin, "only admin can setup metaTransactionProcessors");
        _setMetaTransactionProcessor(metaTransactionProcessor, enabled);
    }

    function _setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) internal {
        _metaTransactionContracts[metaTransactionProcessor] = enabled;
        emit MetaTransactionProcessor(metaTransactionProcessor, enabled);
    }

    /// @dev check whether address `who` is given meta-transaction execution rights.
    /// @param who The address to query.
    /// @return whether the address has meta-transaction execution rights.
    function isMetaTransactionProcessor(address who) external view returns (bool) {
        return _metaTransactionContracts[who];
    }
}

File 12 of 18 : SuperOperators.sol
pragma solidity 0.6.5;

import "./Admin.sol";


contract SuperOperators is Admin {
    mapping(address => bool) internal _superOperators;

    event SuperOperator(address superOperator, bool enabled);

    /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights).
    /// @param superOperator address that will be given/removed superOperator right.
    /// @param enabled set whether the superOperator is enabled or disabled.
    function setSuperOperator(address superOperator, bool enabled) external {
        require(msg.sender == _admin, "only admin is allowed to add super operators");
        _superOperators[superOperator] = enabled;
        emit SuperOperator(superOperator, enabled);
    }

    /// @notice check whether address `who` is given superOperator rights.
    /// @param who The address to query.
    /// @return whether the address has superOperator rights.
    function isSuperOperator(address who) public view returns (bool) {
        return _superOperators[who];
    }
}

File 13 of 18 : ERC20.sol
pragma solidity 0.6.5;


/// @dev see https://eips.ethereum.org/EIPS/eip-20
interface ERC20 {
    /// @notice emitted when tokens are transfered from one address to another.
    /// @param from address from which the token are transfered from (zero means tokens are minted).
    /// @param to destination address which the token are transfered to (zero means tokens are burnt).
    /// @param value amount of tokens transferred.
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice emitted when owner grant transfer rights to another address
    /// @param owner address allowing its token to be transferred.
    /// @param spender address allowed to spend on behalf of `owner`
    /// @param value amount of tokens allowed.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice return the current total amount of tokens owned by all holders.
    /// @return supply total number of tokens held.
    function totalSupply() external view returns (uint256 supply);

    /// @notice return the number of tokens held by a particular address.
    /// @param who address being queried.
    /// @return balance number of token held by that address.
    function balanceOf(address who) external view returns (uint256 balance);

    /// @notice transfer tokens to a specific address.
    /// @param to destination address receiving the tokens.
    /// @param value number of tokens to transfer.
    /// @return success whether the transfer succeeded.
    function transfer(address to, uint256 value) external returns (bool success);

    /// @notice transfer tokens from one address to another.
    /// @param from address tokens will be sent from.
    /// @param to destination address receiving the tokens.
    /// @param value number of tokens to transfer.
    /// @return success whether the transfer succeeded.
    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool success);

    /// @notice approve an address to spend on your behalf.
    /// @param spender address entitled to transfer on your behalf.
    /// @param value amount allowed to be transfered.
    /// @param success whether the approval succeeded.
    function approve(address spender, uint256 value) external returns (bool success);

    /// @notice return the current allowance for a particular owner/spender pair.
    /// @param owner address allowing spender.
    /// @param spender address allowed to spend.
    /// @return amount number of tokens `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender) external view returns (uint256 amount);
}

File 14 of 18 : AddressUtils.sol
pragma solidity 0.6.5;


library AddressUtils {
    function toPayable(address _address) internal pure returns (address payable _payable) {
        return address(uint160(_address));
    }

    function isContract(address addr) internal view returns (bool) {
        // for accounts without code, i.e. `keccak256('')`:
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;

        bytes32 codehash;
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            codehash := extcodehash(addr)
        }
        return (codehash != 0x0 && codehash != accountHash);
    }
}

File 15 of 18 : BytesUtil.sol
pragma solidity 0.6.5;


library BytesUtil {
    function memcpy(
        uint256 dest,
        uint256 src,
        uint256 len
    ) internal pure {
        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint256 mask = 256**(32 - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    function pointerToBytes(uint256 src, uint256 len) internal pure returns (bytes memory) {
        bytes memory ret = new bytes(len);
        uint256 retptr;
        assembly {
            retptr := add(ret, 32)
        }

        memcpy(retptr, src, len);
        return ret;
    }

    function addressToBytes(address a) internal pure returns (bytes memory b) {
        assembly {
            let m := mload(0x40)
            mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, a))
            mstore(0x40, add(m, 52))
            b := m
        }
    }

    function uint256ToBytes(uint256 a) internal pure returns (bytes memory b) {
        assembly {
            let m := mload(0x40)
            mstore(add(m, 32), a)
            mstore(0x40, add(m, 64))
            b := m
        }
    }

    function doFirstParamEqualsAddress(bytes memory data, address _address) internal pure returns (bool) {
        if (data.length < (36 + 32)) {
            return false;
        }
        uint256 value;
        assembly {
            value := mload(add(data, 36))
        }
        return value == uint256(_address);
    }

    function doParamEqualsUInt256(
        bytes memory data,
        uint256 i,
        uint256 value
    ) internal pure returns (bool) {
        if (data.length < (36 + (i + 1) * 32)) {
            return false;
        }
        uint256 offset = 36 + i * 32;
        uint256 valuePresent;
        assembly {
            valuePresent := mload(add(data, offset))
        }
        return valuePresent == value;
    }

    function overrideFirst32BytesWithAddress(bytes memory data, address _address) internal pure returns (bytes memory) {
        uint256 dest;
        assembly {
            dest := add(data, 48)
        } // 48 = 32 (offset) + 4 (func sig) + 12 (address is only 20 bytes)

        bytes memory addressBytes = addressToBytes(_address);
        uint256 src;
        assembly {
            src := add(addressBytes, 32)
        }

        memcpy(dest, src, 20);
        return data;
    }

    function overrideFirstTwo32BytesWithAddressAndInt(
        bytes memory data,
        address _address,
        uint256 _value
    ) internal pure returns (bytes memory) {
        uint256 dest;
        uint256 src;

        assembly {
            dest := add(data, 48)
        } // 48 = 32 (offset) + 4 (func sig) + 12 (address is only 20 bytes)
        bytes memory bbytes = addressToBytes(_address);
        assembly {
            src := add(bbytes, 32)
        }
        memcpy(dest, src, 20);

        assembly {
            dest := add(data, 68)
        } // 48 = 32 (offset) + 4 (func sig) + 32 (next slot)
        bbytes = uint256ToBytes(_value);
        assembly {
            src := add(bbytes, 32)
        }
        memcpy(dest, src, 32);

        return data;
    }
}

File 16 of 18 : ObjectLib32.sol
pragma solidity 0.6.5;

import "./SafeMathWithRequire.sol";


library ObjectLib32 {
    using SafeMathWithRequire for uint256;
    enum Operations {ADD, SUB, REPLACE}
    // Constants regarding bin or chunk sizes for balance packing
    uint256 constant TYPES_BITS_SIZE = 32; // Max size of each object
    uint256 constant TYPES_PER_UINT256 = 256 / TYPES_BITS_SIZE; // Number of types per uint256

    //
    // Objects and Tokens Functions
    //

    /**
     * @dev Return the bin number and index within that bin where ID is
     * @param tokenId Object type
     * @return bin Bin number
     * @return index ID's index within that bin
     */
    function getTokenBinIndex(uint256 tokenId) internal pure returns (uint256 bin, uint256 index) {
        bin = (tokenId * TYPES_BITS_SIZE) / 256;
        index = tokenId % TYPES_PER_UINT256;
        return (bin, index);
    }

    /**
     * @dev update the balance of a type provided in binBalances
     * @param binBalances Uint256 containing the balances of objects
     * @param index Index of the object in the provided bin
     * @param amount Value to update the type balance
     * @param operation Which operation to conduct :
     *     Operations.REPLACE : Replace type balance with amount
     *     Operations.ADD     : ADD amount to type balance
     *     Operations.SUB     : Substract amount from type balance
     */
    function updateTokenBalance(
        uint256 binBalances,
        uint256 index,
        uint256 amount,
        Operations operation
    ) internal pure returns (uint256 newBinBalance) {
        uint256 objectBalance = 0;
        if (operation == Operations.ADD) {
            objectBalance = getValueInBin(binBalances, index);
            newBinBalance = writeValueInBin(binBalances, index, objectBalance.add(amount));
        } else if (operation == Operations.SUB) {
            objectBalance = getValueInBin(binBalances, index);
            require(objectBalance >= amount, "can't substract more than there is");
            newBinBalance = writeValueInBin(binBalances, index, objectBalance.sub(amount));
        } else if (operation == Operations.REPLACE) {
            newBinBalance = writeValueInBin(binBalances, index, amount);
        } else {
            revert("Invalid operation"); // Bad operation
        }

        return newBinBalance;
    }

    /*
     * @dev return value in binValue at position index
     * @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
     * @param index index at which to retrieve value
     * @return Value at given index in bin
     */
    function getValueInBin(uint256 binValue, uint256 index) internal pure returns (uint256) {
        // Mask to retrieve data for a given binData
        uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;

        // Shift amount
        uint256 rightShift = 256 - TYPES_BITS_SIZE * (index + 1);
        return (binValue >> rightShift) & mask;
    }

    /**
     * @dev return the updated binValue after writing amount at index
     * @param binValue uint256 containing the balances of TYPES_PER_UINT256 types
     * @param index Index at which to retrieve value
     * @param amount Value to store at index in bin
     * @return Value at given index in bin
     */
    function writeValueInBin(
        uint256 binValue,
        uint256 index,
        uint256 amount
    ) internal pure returns (uint256) {
        require(amount < 2**TYPES_BITS_SIZE, "Amount to write in bin is too large");

        // Mask to retrieve data for a given binData
        uint256 mask = (uint256(1) << TYPES_BITS_SIZE) - 1;

        // Shift amount
        uint256 leftShift = 256 - TYPES_BITS_SIZE * (index + 1);
        return (binValue & ~(mask << leftShift)) | (amount << leftShift);
    }
}

File 17 of 18 : SafeMath.sol
pragma solidity 0.6.5;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {
    /**
     * @dev Multiplies two numbers, throws on overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        c = a * b;
        assert(c / a == b);
        return c;
    }

    /**
     * @dev Integer division of two numbers, truncating the quotient.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        // uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return a / b;
    }

    /**
     * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    /**
     * @dev Adds two numbers, throws on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = a + b;
        assert(c >= a);
        return c;
    }
}

File 18 of 18 : SafeMathWithRequire.sol
pragma solidity 0.6.5;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert
 */
library SafeMathWithRequire {
    using SafeMathWithRequire for uint256;

    uint256 constant DECIMALS_18 = 1000000000000000000;
    uint256 constant DECIMALS_12 = 1000000000000;
    uint256 constant DECIMALS_9 = 1000000000;
    uint256 constant DECIMALS_6 = 1000000;

    /**
     * @dev Multiplies two numbers, throws on overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        c = a * b;
        require(c / a == b, "overflow");
        return c;
    }

    /**
     * @dev Integer division of two numbers, truncating the quotient.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "divbyzero");
        // uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return a / b;
    }

    /**
     * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "undeflow");
        return a - b;
    }

    /**
     * @dev Adds two numbers, throws on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = a + b;
        require(c >= a, "overflow");
        return c;
    }

    function sqrt6(uint256 a) internal pure returns (uint256 c) {
        a = a.mul(DECIMALS_12);
        uint256 tmp = a.add(1) / 2;
        c = a;
        // tmp cannot be zero unless a = 0 which skip the loop
        while (tmp < c) {
            c = tmp;
            tmp = ((a / tmp) + tmp) / 2;
        }
    }

    function sqrt3(uint256 a) internal pure returns (uint256 c) {
        a = a.mul(DECIMALS_6);
        uint256 tmp = a.add(1) / 2;
        c = a;
        // tmp cannot be zero unless a = 0 which skip the loop
        while (tmp < c) {
            c = tmp;
            tmp = ((a / tmp) + tmp) / 2;
        }
    }

    function cbrt6(uint256 a) internal pure returns (uint256 c) {
        a = a.mul(DECIMALS_18);
        uint256 tmp = a.add(2) / 3;
        c = a;
        // tmp cannot be zero unless a = 0 which skip the loop
        while (tmp < c) {
            c = tmp;
            uint256 tmpSquare = tmp**2;
            require(tmpSquare > tmp, "overflow");
            tmp = ((a / tmpSquare) + (tmp * 2)) / 3;
        }
        return c;
    }

    function cbrt3(uint256 a) internal pure returns (uint256 c) {
        a = a.mul(DECIMALS_9);
        uint256 tmp = a.add(2) / 3;
        c = a;
        // tmp cannot be zero unless a = 0 which skip the loop
        while (tmp < c) {
            c = tmp;
            uint256 tmpSquare = tmp**2;
            require(tmpSquare > tmp, "overflow");
            tmp = ((a / tmpSquare) + (tmp * 2)) / 3;
        }
        return c;
    }

    // TODO test
    function rt6_3(uint256 a) internal pure returns (uint256 c) {
        a = a.mul(DECIMALS_18);
        uint256 tmp = a.add(5) / 6;
        c = a;
        // tmp cannot be zero unless a = 0 which skip the loop
        while (tmp < c) {
            c = tmp;
            uint256 tmpFive = tmp**5;
            require(tmpFive > tmp, "overflow");
            tmp = ((a / tmpFive) + (tmp * 5)) / 6;
        }
    }
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract CatalystRegistry","name":"catalystRegistry","type":"address"},{"internalType":"contract ERC20Extended","name":"sand","type":"address"},{"internalType":"contract AssetToken","name":"asset","type":"address"},{"internalType":"contract GemToken","name":"gems","type":"address"},{"internalType":"address","name":"metaTx","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"feeCollector","type":"address"},{"internalType":"uint256","name":"gemAdditionFee","type":"uint256"},{"internalType":"contract CatalystToken","name":"catalysts","type":"address"},{"internalType":"uint256[4]","name":"bakedInMintdata","type":"uint256[4]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newCollector","type":"address"}],"name":"FeeCollector","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"GemAdditionFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"metaTransactionProcessor","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"MetaTransactionProcessor","type":"event"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256[]","name":"gemIds","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"}],"name":"addGems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"catalystId","type":"uint256"},{"internalType":"uint256[]","name":"gemIds","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"}],"name":"changeCatalyst","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256[]","name":"gemIds","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"}],"name":"extractAndAddGems","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"assetId","type":"uint256"},{"internalType":"uint256","name":"catalystId","type":"uint256"},{"internalType":"uint256[]","name":"gemIds","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"}],"name":"extractAndChangeCatalyst","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"isMetaTransactionProcessor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint40","name":"packId","type":"uint40"},{"internalType":"bytes32","name":"metadataHash","type":"bytes32"},{"internalType":"uint256","name":"catalystId","type":"uint256"},{"internalType":"uint256[]","name":"gemIds","type":"uint256[]"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint40","name":"packId","type":"uint40"},{"internalType":"bytes32","name":"metadataHash","type":"bytes32"},{"internalType":"uint256[]","name":"gemsQuantities","type":"uint256[]"},{"internalType":"uint256[]","name":"catalystsQuantities","type":"uint256[]"},{"components":[{"internalType":"uint256[]","name":"gemIds","type":"uint256[]"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"catalystId","type":"uint256"}],"internalType":"struct CatalystMinter.AssetData[]","name":"assets","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mintMultiple","outputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"setGemAdditionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"metaTransactionProcessor","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setMetaTransactionProcessor","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101a06040523480156200001257600080fd5b5060405162002a6f38038062002a6f8339810160408190526200003591620001e6565b6001600160601b031960608b811b8216610100528a811b821660805289811b821660a05288811b821660c05283901b1660e052600080546001600160a01b0387166001600160a01b031990911617905562000099836001600160e01b03620000f316565b620000ad846001600160e01b036200013516565b620000c38660016001600160e01b036200018216565b80516101205260208101516101405260408101516101605260600151610180525062000375975050505050505050565b60038190556040517fd1f666866bc45129492a51e011fa6b5a25db558c3bbdffbaf0dbbdbc57347403906200012a90839062000353565b60405180910390a150565b600280546001600160a01b0319166001600160a01b0383161790556040517ffa62ad95255db03db5eaabed7a43b93f377948b56cea8e12a747b6565582ed5e906200012a90839062000324565b6001600160a01b03821660009081526001602052604090819020805460ff1916831515179055517fb21eb88b4e33b3f1281830a7178d74d8aa73220416215726b68ae23d539515cb90620001da908490849062000338565b60405180910390a15050565b6000806000806000806000806000806101a08b8d03121562000206578586fd5b8a5162000213816200035c565b60208c0151909a5062000226816200035c565b60408c015190995062000239816200035c565b60608c01519098506200024c816200035c565b60808c01519097506200025f816200035c565b60a08c015190965062000272816200035c565b60c08c015190955062000285816200035c565b60e08c01516101008d01519195509350620002a0816200035c565b915061013f8b018c13620002b2578081fd5b604051608081016001600160401b0381118282101715620002d1578283fd5b604052806101208d016101a08e018f1015620002eb578384fd5b835b60048110156200030e578151835260209283019290910190600101620002ed565b505050809150509295989b9194979a5092959850565b6001600160a01b0391909116815260200190565b6001600160a01b039290921682521515602082015260400190565b90815260200190565b6001600160a01b03811681146200037257600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205161014051610160516101805161264c62000423600039806110b352508061108152508061104f52508061100e525080610479528061090c5280610c8252806116cc525080610a555280611107528061187852508061122c52806112f052508061039b528061056b52806106e85280610f975280611627525080610e1c5280610ebd525061264c6000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063c8929ab011610081578063e8aab9571161005b578063e8aab957146101b8578063eb94619f146101cb578063f2c0d1e7146101de576100d4565b8063c8929ab014610165578063d2fcbd2e14610178578063dc5074af14610198576100d4565b80638f283970116100b25780638f2839701461011f5780638f42403a14610132578063a42dce8014610152576100d4565b80636e9960c3146100d95780637011ead3146100f75780638a04af6a1461010c575b600080fd5b6100e16101f1565b6040516100ee9190612001565b60405180910390f35b61010a610105366004611bd5565b610200565b005b61010a61011a366004611b9e565b610251565b61010a61012d366004611b7c565b610292565b610145610140366004611da2565b61033d565b6040516100ee91906124f2565b61010a610160366004611b7c565b6104f9565b610145610173366004611c44565b61052f565b61018b610186366004611cbb565b610643565b6040516100ee919061221a565b6101ab6101a6366004611b7c565b61068e565b6040516100ee919061222d565b6101456101c6366004611bd5565b6106ac565b6101456101d9366004611c44565b6107be565b61010a6101ec366004611f97565b610816565b6000546001600160a01b031690565b61020a8582610849565b61024a85858585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508792506108b1915050565b5050505050565b6000546001600160a01b031633146102845760405162461bcd60e51b815260040161027b90612382565b60405180910390fd5b61028e82826109a5565b5050565b6000546001600160a01b031633146102bc5760405162461bcd60e51b815260040161027b9061244d565b6000546040517f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f916102fb916001600160a01b03909116908490612015565b60405180910390a1600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b60006103498b85610849565b6103538b89610a25565b60006103958c8a8a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250610ac5915050565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c50a4eb98e8e8e8b60008c8c8c6040518963ffffffff1660e01b81526004016103f49897969594939291906121ab565b602060405180830381600087803b15801561040e57600080fd5b505af1158015610422573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104469190611faf565b6040517f1b74ea650000000000000000000000000000000000000000000000000000000081529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631b74ea65906104b69084908e9087908f908f9060040161251c565b600060405180830381600087803b1580156104d057600080fd5b505af11580156104e4573d6000803e3d6000fd5b50929f9e505050505050505050505050505050565b6000546001600160a01b031633146105235760405162461bcd60e51b815260040161027b90612314565b61052c81610b63565b50565b600061053b8783610849565b6040517fff23be530000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ff23be53906105a4908a908a9082906004016120bb565b602060405180830381600087803b1580156105be57600080fd5b505af11580156105d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f69190611faf565b9050610639878287878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250610bd1915050565b9695505050505050565b60608351600014156106675760405162461bcd60e51b815260040161027b90612238565b6106718984610849565b6106818989898989898989610d03565b9998505050505050505050565b6001600160a01b031660009081526001602052604090205460ff1690565b60006106b88683610849565b6040517fff23be530000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ff23be5390610721908990899082906004016120bb565b602060405180830381600087803b15801561073b57600080fd5b505af115801561074f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107739190611faf565b90506107b586828686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506108b1915050565b95945050505050565b60006107ca8783610849565b61080b878787878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250610bd1915050565b509395945050505050565b6000546001600160a01b031633146108405760405162461bcd60e51b815260040161027b90612314565b61052c81610d46565b6001600160a01b03811661086f5760405162461bcd60e51b815260040161027b9061226f565b6001600160a01b03821633148061089557503360009081526001602052604090205460ff165b61028e5760405162461bcd60e51b815260040161027b906123df565b6b80000000000000000000000083166108dc5760405162461bcd60e51b815260040161027b906122a6565b6040517fb07fdd1f0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b07fdd1f9061094390869086906004016124fb565b600060405180830381600087803b15801561095d57600080fd5b505af1158015610971573d6000803e3d6000fd5b505050506109948461098f6003548551610d7b90919063ffffffff16565b610dbc565b61099f848285610f4e565b50505050565b6001600160a01b0382166000908152600160205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016831515179055517fb21eb88b4e33b3f1281830a7178d74d8aa73220416215726b68ae23d539515cb90610a199084908490612087565b60405180910390a15050565b6040517f124d91e50000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063124d91e590610a8f90859085906001906004016120de565b600060405180830381600087803b158015610aa957600080fd5b505af1158015610abd573d6000803e3d6000fd5b505050505050565b6000806000806000610ad688610ffe565b509350935093509350858361ffff1611158015610af757508161ffff168611155b610b135760405162461bcd60e51b815260040161027b90612484565b8361ffff1687511115610b385760405162461bcd60e51b815260040161027b90612416565b610b4289886111a0565b610b568961098f888463ffffffff610d7b16565b5091979650505050505050565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790556040517ffa62ad95255db03db5eaabed7a43b93f377948b56cea8e12a747b6565582ed5e90610bc6908390612001565b60405180910390a150565b6b8000000000000000000000008416610bfc5760405162461bcd60e51b815260040161027b906122a6565b610c068584610a25565b600080610c1285610ffe565b945050505091508161ffff1684511115610c3e5760405162461bcd60e51b815260040161027b90612416565b610c488785611265565b610c528782610dbc565b6040517f1b74ea650000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631b74ea6590610cbd908990899087908a90600401612588565b600060405180830381600087803b158015610cd757600080fd5b505af1158015610ceb573d6000803e3d6000fd5b50505050610cfa878488610f4e565b50505050505050565b60606000606080610d168c8a8a8a611329565b925092509250610d268c84610dbc565b610d368c8c8c8a86868c8c6115f4565b9c9b505050505050505050505050565b60038190556040517fd1f666866bc45129492a51e011fa6b5a25db558c3bbdffbaf0dbbdbc5734740390610bc69083906124f2565b600082610d8a57506000610db6565b5081810281838281610d9857fe5b0414610db65760405162461bcd60e51b815260040161027b906124bb565b92915050565b6002546001600160a01b03168015801590610dd657508115155b15610f49576001600160a01b038181161415610e8a576040517f1dd319cb0000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631dd319cb90610e5390869086906004016120a2565b600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b50505050610f49565b6002546040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116926323b872dd92610ef79288921690879060040161202f565b602060405180830381600087803b158015610f1157600080fd5b505af1158015610f25573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099f9190611f1e565b505050565b816001600160a01b0316836001600160a01b031614610f49576040517f42842e0e0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906342842e0e90610fd09086908690869060040161202f565b600060405180830381600087803b158015610fea57600080fd5b505af1158015610cfa573d6000803e3d6000fd5b60008080808085611041576110327f00000000000000000000000000000000000000000000000000000000000000006117bb565b94509450945094509450611197565b8560011415611073576110327f00000000000000000000000000000000000000000000000000000000000000006117bb565b85600214156110a5576110327f00000000000000000000000000000000000000000000000000000000000000006117bb565b85600314156110d7576110327f00000000000000000000000000000000000000000000000000000000000000006117bb565b6040517f73f164250000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906373f164259061113c9089906004016124f2565b60a06040518083038186803b15801561115457600080fd5b505afa158015611168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118c9190611f3a565b945094509450945094505b91939590929450565b6060815167ffffffffffffffff811180156111ba57600080fd5b506040519080825280602002602001820160405280156111e4578160200160208202803683370190505b50905060005b825181101561121457600182828151811061120157fe5b60209081029190910101526001016111ea565b50604051632014d24d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638053493490610fd090869086908690600401612053565b6060815167ffffffffffffffff8111801561127f57600080fd5b506040519080825280602002602001820160405280156112a9578160200160208202803683370190505b50905060005b81518110156112d857808282815181106112c557fe5b60209081029190910101526001016112af565b50604051632014d24d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638053493490610fd090869085908790600401612053565b600060608061133887866117ed565b6113428787611265565b835167ffffffffffffffff8111801561135a57600080fd5b50604051908082528060200260200182016040528015611384578160200160208202803683370190505b509150835167ffffffffffffffff8111801561139f57600080fd5b506040519080825280602002602001820160405280156113c9578160200160208202803683370190505b50905060005b84518110156115e957858582815181106113e557fe5b602002602001015160400151815181106113fb57fe5b6020026020010151600014156114235760405162461bcd60e51b815260040161027b906122dd565b8585828151811061143057fe5b6020026020010151604001518151811061144657fe5b602002602001018051809190600190038152505061147b8786838151811061146a57fe5b6020026020010151600001516118b1565b96506000806000806114a389868151811061149257fe5b602002602001015160400151610ffe565b5093509350935093508885815181106114b857fe5b6020026020010151602001518361ffff16111580156114f257508161ffff168986815181106114e357fe5b60200260200101516020015111155b61150e5760405162461bcd60e51b815260040161027b90612484565b8361ffff1689868151811061151f57fe5b60200260200101516000015151111561154a5760405162461bcd60e51b815260040161027b90612416565b8386868151811061155757fe5b602002602001019061ffff16908161ffff168152505088858151811061157957fe5b60200260200101516020015187868151811061159157fe5b6020026020010181815250506115d76115ca8a87815181106115af57fe5b60200260200101516020015183610d7b90919063ffffffff16565b899063ffffffff61196516565b975050600190930192506113cf915050565b509450945094915050565b6040517ff0bc00d80000000000000000000000000000000000000000000000000000000081526060906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f0bc00d890611666908c908c908c908b908a908a906004016120ff565b600060405180830381600087803b15801561168057600080fd5b505af1158015611694573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526116bc9190810190611e8b565b905060005b81518110156117ae577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631b74ea6583838151811061170557fe5b602002602001015189848151811061171957fe5b60200260200101516040015188858151811061173157fe5b60200260200101518b868151811061174557fe5b6020026020010151600001516040518563ffffffff1660e01b81526004016117709493929190612588565b600060405180830381600087803b15801561178a57600080fd5b505af115801561179e573d6000803e3d6000fd5b5050600190920191506116c19050565b5098975050505050505050565b60f081901c9161ffff60e083901c81169260d081901c909116916affffffffffffffffffffff607883901c8116921690565b6060815167ffffffffffffffff8111801561180757600080fd5b50604051908082528060200260200182016040528015611831578160200160208202803683370190505b50905060005b8151811015611860578082828151811061184d57fe5b6020908102919091010152600101611837565b50604051632014d24d60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638053493490610fd090869085908790600401612053565b606060005b825181101561195d57838382815181106118cc57fe5b6020026020010151815181106118de57fe5b6020026020010151600014156119065760405162461bcd60e51b815260040161027b9061234b565b8383828151811061191357fe5b60200260200101518151811061192557fe5b6020908102919091010180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190526001016118b6565b509192915050565b81810182811015610db65760405162461bcd60e51b815260040161027b906124bb565b80356001600160a01b0381168114610db657600080fd5b600082601f8301126119af578081fd5b81356119c26119bd826125d8565b6125b1565b818152915060208083019084810160005b84811015611a4c5781358701606080601f19838c030112156119f457600080fd5b6119fd816125b1565b8583013567ffffffffffffffff811115611a1657600080fd5b611a248c8883870101611aa0565b82525060408381013587830152919092013590820152845292820192908201906001016119d3565b505050505092915050565b60008083601f840112611a68578081fd5b50813567ffffffffffffffff811115611a7f578182fd5b6020830191508360208083028501011115611a9957600080fd5b9250929050565b600082601f830112611ab0578081fd5b8135611abe6119bd826125d8565b818152915060208083019084810181840286018201871015611adf57600080fd5b60005b84811015611a4c57813584529282019290820190600101611ae2565b600082601f830112611b0e578081fd5b813567ffffffffffffffff811115611b24578182fd5b611b376020601f19601f840116016125b1565b9150808252836020828501011115611b4e57600080fd5b8060208401602084013760009082016020015292915050565b803564ffffffffff81168114610db657600080fd5b600060208284031215611b8d578081fd5b611b978383611988565b9392505050565b60008060408385031215611bb0578081fd5b611bba8484611988565b91506020830135611bca816125f8565b809150509250929050565b600080600080600060808688031215611bec578081fd5b611bf68787611988565b945060208601359350604086013567ffffffffffffffff811115611c18578182fd5b611c2488828901611a57565b9094509250611c3890508760608801611988565b90509295509295909350565b60008060008060008060a08789031215611c5c578081fd5b611c668888611988565b95506020870135945060408701359350606087013567ffffffffffffffff811115611c8f578182fd5b611c9b89828a01611a57565b9094509250611caf90508860808901611988565b90509295509295509295565b600080600080600080600080610100898b031215611cd7578182fd5b611ce18a8a611988565b9750611cf08a60208b01611b67565b965060408901359550606089013567ffffffffffffffff80821115611d13578384fd5b611d1f8c838d01611aa0565b965060808b0135915080821115611d34578384fd5b611d408c838d01611aa0565b955060a08b0135915080821115611d55578384fd5b611d618c838d0161199f565b9450611d708c60c08d01611988565b935060e08b0135915080821115611d85578283fd5b50611d928b828c01611afe565b9150509295985092959890939650565b6000806000806000806000806000806101008b8d031215611dc1578384fd5b611dcb8c8c611988565b9950611dda8c60208d01611b67565b985060408b0135975060608b0135965060808b013567ffffffffffffffff80821115611e04578586fd5b611e108e838f01611a57565b909850965060a08d01359550611e298e60c08f01611988565b945060e08d0135915080821115611e3e578384fd5b818d018e601f820112611e4f578485fd5b8035925081831115611e5f578485fd5b8e6020848301011115611e70578485fd5b6020810194505050809150509295989b9194979a5092959850565b60006020808385031215611e9d578182fd5b825167ffffffffffffffff811115611eb3578283fd5b80840185601f820112611ec4578384fd5b80519150611ed46119bd836125d8565b8281528381019082850185850284018601891015611ef0578687fd5b8693505b84841015611f12578051835260019390930192918501918501611ef4565b50979650505050505050565b600060208284031215611f2f578081fd5b8151611b97816125f8565b600080600080600060a08688031215611f51578283fd5b8551611f5c81612606565b6020870151909550611f6d81612606565b6040870151909450611f7e81612606565b6060870151608090970151959894975095949392505050565b600060208284031215611fa8578081fd5b5035919050565b600060208284031215611fc0578081fd5b5051919050565b6000815180845260208085019450808401835b83811015611ff657815187529582019590820190600101611fda565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006001600160a01b0385168252606060208301526120756060830185611fc7565b82810360408401526106398185611fc7565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b039390931683526020830191909152604082015260600190565b60006001600160a01b038089168352602064ffffffffff89168185015287604085015260e0606085015261213660e0850188611fc7565b848103608086015261214884826124f2565b92871660a08601525083820360c085015284518083529183905b8382101561217f5786820183015181830184015290820190612162565b8382111561218f57848385830101525b601f93909301601f191692909201019998505050505050505050565b60006001600160a01b03808b16835264ffffffffff8a16602084015288604084015287606084015260ff8716608084015280861660a08401525060e060c08301528260e0830152610100838582850137828401810191909152601f909201601f19160101979650505050505050565b600060208252611b976020830184611fc7565b901515815260200190565b60208082526010908201527f494e56414c49445f305f41535345545300000000000000000000000000000000604082015260600190565b60208082526017908201527f494e56414c49445f544f5f5a45524f5f41444452455353000000000000000000604082015260600190565b6020808252600f908201527f494e56414c49445f4e4f545f4e46540000000000000000000000000000000000604082015260600190565b6020808252601b908201527f494e56414c49445f434154414c5953545f4e4f545f454e4f5547480000000000604082015260600190565b60208082526014908201527f4e4f545f415554484f52495a45445f41444d494e000000000000000000000000604082015260600190565b60208082526017908201527f494e56414c49445f47454d535f4e4f545f454e4f554748000000000000000000604082015260600190565b6020808252602e908201527f6f6e6c792061646d696e2063616e207365747570206d6574615472616e73616360408201527f74696f6e50726f636573736f7273000000000000000000000000000000000000606082015260800190565b6020808252600a908201527f4e4f545f53454e44455200000000000000000000000000000000000000000000604082015260600190565b60208082526015908201527f494e56414c49445f47454d535f544f4f5f4d414e590000000000000000000000604082015260600190565b6020808252601b908201527f6f6e6c792061646d696e2063616e206368616e67652061646d696e0000000000604082015260600190565b60208082526010908201527f494e56414c49445f5155414e5449545900000000000000000000000000000000604082015260600190565b60208082526008908201527f6f766572666c6f77000000000000000000000000000000000000000000000000604082015260600190565b90815260200190565b6000838252604060208301526125146040830184611fc7565b949350505050565b600086825285602083015261ffff85166040830152608060608301528260808301527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561256a578081fd5b60208302808560a08501379190910160a00190815295945050505050565b600085825284602083015261ffff84166040830152608060608301526106396080830184611fc7565b60405181810167ffffffffffffffff811182821017156125d057600080fd5b604052919050565b600067ffffffffffffffff8211156125ee578081fd5b5060209081020190565b801515811461052c57600080fd5b61ffff8116811461052c57600080fdfea2646970667358221220347d6f2f0399a2fe8cefd458e36464b0015333fbfa3536eded7dc33372af409764736f6c63430006050033000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa70000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d0000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db50000000000000000000000008ff2611da386de427fc96a8073963619c5851ba50000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d0000000000000000000000000eaa0993e1d21c2103e4f172a20d29371fbaf6d0600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000002fc246149b4b8d7bcef6188a10af1791380227f100010001ffff0000000de0b6b3a7640000000000000000000de0b6b3a764000000020001ffff0000003782dace9d900000000000000000003782dace9d90000000030001ffff0000008ac7230489e80000000000000000008ac7230489e8000000040001ffff00000ad78ebc5ac62000000000000000000ad78ebc5ac6200000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100d45760003560e01c8063c8929ab011610081578063e8aab9571161005b578063e8aab957146101b8578063eb94619f146101cb578063f2c0d1e7146101de576100d4565b8063c8929ab014610165578063d2fcbd2e14610178578063dc5074af14610198576100d4565b80638f283970116100b25780638f2839701461011f5780638f42403a14610132578063a42dce8014610152576100d4565b80636e9960c3146100d95780637011ead3146100f75780638a04af6a1461010c575b600080fd5b6100e16101f1565b6040516100ee9190612001565b60405180910390f35b61010a610105366004611bd5565b610200565b005b61010a61011a366004611b9e565b610251565b61010a61012d366004611b7c565b610292565b610145610140366004611da2565b61033d565b6040516100ee91906124f2565b61010a610160366004611b7c565b6104f9565b610145610173366004611c44565b61052f565b61018b610186366004611cbb565b610643565b6040516100ee919061221a565b6101ab6101a6366004611b7c565b61068e565b6040516100ee919061222d565b6101456101c6366004611bd5565b6106ac565b6101456101d9366004611c44565b6107be565b61010a6101ec366004611f97565b610816565b6000546001600160a01b031690565b61020a8582610849565b61024a85858585808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508792506108b1915050565b5050505050565b6000546001600160a01b031633146102845760405162461bcd60e51b815260040161027b90612382565b60405180910390fd5b61028e82826109a5565b5050565b6000546001600160a01b031633146102bc5760405162461bcd60e51b815260040161027b9061244d565b6000546040517f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f916102fb916001600160a01b03909116908490612015565b60405180910390a1600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b60006103498b85610849565b6103538b89610a25565b60006103958c8a8a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250610ac5915050565b905060007f000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db56001600160a01b031663c50a4eb98e8e8e8b60008c8c8c6040518963ffffffff1660e01b81526004016103f49897969594939291906121ab565b602060405180830381600087803b15801561040e57600080fd5b505af1158015610422573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104469190611faf565b6040517f1b74ea650000000000000000000000000000000000000000000000000000000081529091506001600160a01b037f000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa71690631b74ea65906104b69084908e9087908f908f9060040161251c565b600060405180830381600087803b1580156104d057600080fd5b505af11580156104e4573d6000803e3d6000fd5b50929f9e505050505050505050505050505050565b6000546001600160a01b031633146105235760405162461bcd60e51b815260040161027b90612314565b61052c81610b63565b50565b600061053b8783610849565b6040517fff23be530000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db5169063ff23be53906105a4908a908a9082906004016120bb565b602060405180830381600087803b1580156105be57600080fd5b505af11580156105d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f69190611faf565b9050610639878287878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250610bd1915050565b9695505050505050565b60608351600014156106675760405162461bcd60e51b815260040161027b90612238565b6106718984610849565b6106818989898989898989610d03565b9998505050505050505050565b6001600160a01b031660009081526001602052604090205460ff1690565b60006106b88683610849565b6040517fff23be530000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db5169063ff23be5390610721908990899082906004016120bb565b602060405180830381600087803b15801561073b57600080fd5b505af115801561074f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107739190611faf565b90506107b586828686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506108b1915050565b95945050505050565b60006107ca8783610849565b61080b878787878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250610bd1915050565b509395945050505050565b6000546001600160a01b031633146108405760405162461bcd60e51b815260040161027b90612314565b61052c81610d46565b6001600160a01b03811661086f5760405162461bcd60e51b815260040161027b9061226f565b6001600160a01b03821633148061089557503360009081526001602052604090205460ff165b61028e5760405162461bcd60e51b815260040161027b906123df565b6b80000000000000000000000083166108dc5760405162461bcd60e51b815260040161027b906122a6565b6040517fb07fdd1f0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa7169063b07fdd1f9061094390869086906004016124fb565b600060405180830381600087803b15801561095d57600080fd5b505af1158015610971573d6000803e3d6000fd5b505050506109948461098f6003548551610d7b90919063ffffffff16565b610dbc565b61099f848285610f4e565b50505050565b6001600160a01b0382166000908152600160205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016831515179055517fb21eb88b4e33b3f1281830a7178d74d8aa73220416215726b68ae23d539515cb90610a199084908490612087565b60405180910390a15050565b6040517f124d91e50000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000002fc246149b4b8d7bcef6188a10af1791380227f1169063124d91e590610a8f90859085906001906004016120de565b600060405180830381600087803b158015610aa957600080fd5b505af1158015610abd573d6000803e3d6000fd5b505050505050565b6000806000806000610ad688610ffe565b509350935093509350858361ffff1611158015610af757508161ffff168611155b610b135760405162461bcd60e51b815260040161027b90612484565b8361ffff1687511115610b385760405162461bcd60e51b815260040161027b90612416565b610b4289886111a0565b610b568961098f888463ffffffff610d7b16565b5091979650505050505050565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790556040517ffa62ad95255db03db5eaabed7a43b93f377948b56cea8e12a747b6565582ed5e90610bc6908390612001565b60405180910390a150565b6b8000000000000000000000008416610bfc5760405162461bcd60e51b815260040161027b906122a6565b610c068584610a25565b600080610c1285610ffe565b945050505091508161ffff1684511115610c3e5760405162461bcd60e51b815260040161027b90612416565b610c488785611265565b610c528782610dbc565b6040517f1b74ea650000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa71690631b74ea6590610cbd908990899087908a90600401612588565b600060405180830381600087803b158015610cd757600080fd5b505af1158015610ceb573d6000803e3d6000fd5b50505050610cfa878488610f4e565b50505050505050565b60606000606080610d168c8a8a8a611329565b925092509250610d268c84610dbc565b610d368c8c8c8a86868c8c6115f4565b9c9b505050505050505050505050565b60038190556040517fd1f666866bc45129492a51e011fa6b5a25db558c3bbdffbaf0dbbdbc5734740390610bc69083906124f2565b600082610d8a57506000610db6565b5081810281838281610d9857fe5b0414610db65760405162461bcd60e51b815260040161027b906124bb565b92915050565b6002546001600160a01b03168015801590610dd657508115155b15610f49576001600160a01b038181161415610e8a576040517f1dd319cb0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d01690631dd319cb90610e5390869086906004016120a2565b600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b50505050610f49565b6002546040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d08116926323b872dd92610ef79288921690879060040161202f565b602060405180830381600087803b158015610f1157600080fd5b505af1158015610f25573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099f9190611f1e565b505050565b816001600160a01b0316836001600160a01b031614610f49576040517f42842e0e0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db516906342842e0e90610fd09086908690869060040161202f565b600060405180830381600087803b158015610fea57600080fd5b505af1158015610cfa573d6000803e3d6000fd5b60008080808085611041576110327f00010001ffff0000000de0b6b3a7640000000000000000000de0b6b3a76400006117bb565b94509450945094509450611197565b8560011415611073576110327f00020001ffff0000003782dace9d900000000000000000003782dace9d9000006117bb565b85600214156110a5576110327f00030001ffff0000008ac7230489e80000000000000000008ac7230489e800006117bb565b85600314156110d7576110327f00040001ffff00000ad78ebc5ac62000000000000000000ad78ebc5ac62000006117bb565b6040517f73f164250000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000002fc246149b4b8d7bcef6188a10af1791380227f116906373f164259061113c9089906004016124f2565b60a06040518083038186803b15801561115457600080fd5b505afa158015611168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118c9190611f3a565b945094509450945094505b91939590929450565b6060815167ffffffffffffffff811180156111ba57600080fd5b506040519080825280602002602001820160405280156111e4578160200160208202803683370190505b50905060005b825181101561121457600182828151811061120157fe5b60209081029190910101526001016111ea565b50604051632014d24d60e21b81526001600160a01b037f0000000000000000000000008ff2611da386de427fc96a8073963619c5851ba51690638053493490610fd090869086908690600401612053565b6060815167ffffffffffffffff8111801561127f57600080fd5b506040519080825280602002602001820160405280156112a9578160200160208202803683370190505b50905060005b81518110156112d857808282815181106112c557fe5b60209081029190910101526001016112af565b50604051632014d24d60e21b81526001600160a01b037f0000000000000000000000008ff2611da386de427fc96a8073963619c5851ba51690638053493490610fd090869085908790600401612053565b600060608061133887866117ed565b6113428787611265565b835167ffffffffffffffff8111801561135a57600080fd5b50604051908082528060200260200182016040528015611384578160200160208202803683370190505b509150835167ffffffffffffffff8111801561139f57600080fd5b506040519080825280602002602001820160405280156113c9578160200160208202803683370190505b50905060005b84518110156115e957858582815181106113e557fe5b602002602001015160400151815181106113fb57fe5b6020026020010151600014156114235760405162461bcd60e51b815260040161027b906122dd565b8585828151811061143057fe5b6020026020010151604001518151811061144657fe5b602002602001018051809190600190038152505061147b8786838151811061146a57fe5b6020026020010151600001516118b1565b96506000806000806114a389868151811061149257fe5b602002602001015160400151610ffe565b5093509350935093508885815181106114b857fe5b6020026020010151602001518361ffff16111580156114f257508161ffff168986815181106114e357fe5b60200260200101516020015111155b61150e5760405162461bcd60e51b815260040161027b90612484565b8361ffff1689868151811061151f57fe5b60200260200101516000015151111561154a5760405162461bcd60e51b815260040161027b90612416565b8386868151811061155757fe5b602002602001019061ffff16908161ffff168152505088858151811061157957fe5b60200260200101516020015187868151811061159157fe5b6020026020010181815250506115d76115ca8a87815181106115af57fe5b60200260200101516020015183610d7b90919063ffffffff16565b899063ffffffff61196516565b975050600190930192506113cf915050565b509450945094915050565b6040517ff0bc00d80000000000000000000000000000000000000000000000000000000081526060906001600160a01b037f000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db5169063f0bc00d890611666908c908c908c908b908a908a906004016120ff565b600060405180830381600087803b15801561168057600080fd5b505af1158015611694573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526116bc9190810190611e8b565b905060005b81518110156117ae577f000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa76001600160a01b0316631b74ea6583838151811061170557fe5b602002602001015189848151811061171957fe5b60200260200101516040015188858151811061173157fe5b60200260200101518b868151811061174557fe5b6020026020010151600001516040518563ffffffff1660e01b81526004016117709493929190612588565b600060405180830381600087803b15801561178a57600080fd5b505af115801561179e573d6000803e3d6000fd5b5050600190920191506116c19050565b5098975050505050505050565b60f081901c9161ffff60e083901c81169260d081901c909116916affffffffffffffffffffff607883901c8116921690565b6060815167ffffffffffffffff8111801561180757600080fd5b50604051908082528060200260200182016040528015611831578160200160208202803683370190505b50905060005b8151811015611860578082828151811061184d57fe5b6020908102919091010152600101611837565b50604051632014d24d60e21b81526001600160a01b037f0000000000000000000000002fc246149b4b8d7bcef6188a10af1791380227f11690638053493490610fd090869085908790600401612053565b606060005b825181101561195d57838382815181106118cc57fe5b6020026020010151815181106118de57fe5b6020026020010151600014156119065760405162461bcd60e51b815260040161027b9061234b565b8383828151811061191357fe5b60200260200101518151811061192557fe5b6020908102919091010180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190526001016118b6565b509192915050565b81810182811015610db65760405162461bcd60e51b815260040161027b906124bb565b80356001600160a01b0381168114610db657600080fd5b600082601f8301126119af578081fd5b81356119c26119bd826125d8565b6125b1565b818152915060208083019084810160005b84811015611a4c5781358701606080601f19838c030112156119f457600080fd5b6119fd816125b1565b8583013567ffffffffffffffff811115611a1657600080fd5b611a248c8883870101611aa0565b82525060408381013587830152919092013590820152845292820192908201906001016119d3565b505050505092915050565b60008083601f840112611a68578081fd5b50813567ffffffffffffffff811115611a7f578182fd5b6020830191508360208083028501011115611a9957600080fd5b9250929050565b600082601f830112611ab0578081fd5b8135611abe6119bd826125d8565b818152915060208083019084810181840286018201871015611adf57600080fd5b60005b84811015611a4c57813584529282019290820190600101611ae2565b600082601f830112611b0e578081fd5b813567ffffffffffffffff811115611b24578182fd5b611b376020601f19601f840116016125b1565b9150808252836020828501011115611b4e57600080fd5b8060208401602084013760009082016020015292915050565b803564ffffffffff81168114610db657600080fd5b600060208284031215611b8d578081fd5b611b978383611988565b9392505050565b60008060408385031215611bb0578081fd5b611bba8484611988565b91506020830135611bca816125f8565b809150509250929050565b600080600080600060808688031215611bec578081fd5b611bf68787611988565b945060208601359350604086013567ffffffffffffffff811115611c18578182fd5b611c2488828901611a57565b9094509250611c3890508760608801611988565b90509295509295909350565b60008060008060008060a08789031215611c5c578081fd5b611c668888611988565b95506020870135945060408701359350606087013567ffffffffffffffff811115611c8f578182fd5b611c9b89828a01611a57565b9094509250611caf90508860808901611988565b90509295509295509295565b600080600080600080600080610100898b031215611cd7578182fd5b611ce18a8a611988565b9750611cf08a60208b01611b67565b965060408901359550606089013567ffffffffffffffff80821115611d13578384fd5b611d1f8c838d01611aa0565b965060808b0135915080821115611d34578384fd5b611d408c838d01611aa0565b955060a08b0135915080821115611d55578384fd5b611d618c838d0161199f565b9450611d708c60c08d01611988565b935060e08b0135915080821115611d85578283fd5b50611d928b828c01611afe565b9150509295985092959890939650565b6000806000806000806000806000806101008b8d031215611dc1578384fd5b611dcb8c8c611988565b9950611dda8c60208d01611b67565b985060408b0135975060608b0135965060808b013567ffffffffffffffff80821115611e04578586fd5b611e108e838f01611a57565b909850965060a08d01359550611e298e60c08f01611988565b945060e08d0135915080821115611e3e578384fd5b818d018e601f820112611e4f578485fd5b8035925081831115611e5f578485fd5b8e6020848301011115611e70578485fd5b6020810194505050809150509295989b9194979a5092959850565b60006020808385031215611e9d578182fd5b825167ffffffffffffffff811115611eb3578283fd5b80840185601f820112611ec4578384fd5b80519150611ed46119bd836125d8565b8281528381019082850185850284018601891015611ef0578687fd5b8693505b84841015611f12578051835260019390930192918501918501611ef4565b50979650505050505050565b600060208284031215611f2f578081fd5b8151611b97816125f8565b600080600080600060a08688031215611f51578283fd5b8551611f5c81612606565b6020870151909550611f6d81612606565b6040870151909450611f7e81612606565b6060870151608090970151959894975095949392505050565b600060208284031215611fa8578081fd5b5035919050565b600060208284031215611fc0578081fd5b5051919050565b6000815180845260208085019450808401835b83811015611ff657815187529582019590820190600101611fda565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006001600160a01b0385168252606060208301526120756060830185611fc7565b82810360408401526106398185611fc7565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b039390931683526020830191909152604082015260600190565b60006001600160a01b038089168352602064ffffffffff89168185015287604085015260e0606085015261213660e0850188611fc7565b848103608086015261214884826124f2565b92871660a08601525083820360c085015284518083529183905b8382101561217f5786820183015181830184015290820190612162565b8382111561218f57848385830101525b601f93909301601f191692909201019998505050505050505050565b60006001600160a01b03808b16835264ffffffffff8a16602084015288604084015287606084015260ff8716608084015280861660a08401525060e060c08301528260e0830152610100838582850137828401810191909152601f909201601f19160101979650505050505050565b600060208252611b976020830184611fc7565b901515815260200190565b60208082526010908201527f494e56414c49445f305f41535345545300000000000000000000000000000000604082015260600190565b60208082526017908201527f494e56414c49445f544f5f5a45524f5f41444452455353000000000000000000604082015260600190565b6020808252600f908201527f494e56414c49445f4e4f545f4e46540000000000000000000000000000000000604082015260600190565b6020808252601b908201527f494e56414c49445f434154414c5953545f4e4f545f454e4f5547480000000000604082015260600190565b60208082526014908201527f4e4f545f415554484f52495a45445f41444d494e000000000000000000000000604082015260600190565b60208082526017908201527f494e56414c49445f47454d535f4e4f545f454e4f554748000000000000000000604082015260600190565b6020808252602e908201527f6f6e6c792061646d696e2063616e207365747570206d6574615472616e73616360408201527f74696f6e50726f636573736f7273000000000000000000000000000000000000606082015260800190565b6020808252600a908201527f4e4f545f53454e44455200000000000000000000000000000000000000000000604082015260600190565b60208082526015908201527f494e56414c49445f47454d535f544f4f5f4d414e590000000000000000000000604082015260600190565b6020808252601b908201527f6f6e6c792061646d696e2063616e206368616e67652061646d696e0000000000604082015260600190565b60208082526010908201527f494e56414c49445f5155414e5449545900000000000000000000000000000000604082015260600190565b60208082526008908201527f6f766572666c6f77000000000000000000000000000000000000000000000000604082015260600190565b90815260200190565b6000838252604060208301526125146040830184611fc7565b949350505050565b600086825285602083015261ffff85166040830152608060608301528260808301527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561256a578081fd5b60208302808560a08501379190910160a00190815295945050505050565b600085825284602083015261ffff84166040830152608060608301526106396080830184611fc7565b60405181810167ffffffffffffffff811182821017156125d057600080fd5b604052919050565b600067ffffffffffffffff8211156125ee578081fd5b5060209081020190565b801515811461052c57600080fd5b61ffff8116811461052c57600080fdfea2646970667358221220347d6f2f0399a2fe8cefd458e36464b0015333fbfa3536eded7dc33372af409764736f6c63430006050033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa70000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d0000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db50000000000000000000000008ff2611da386de427fc96a8073963619c5851ba50000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d0000000000000000000000000eaa0993e1d21c2103e4f172a20d29371fbaf6d0600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000002fc246149b4b8d7bcef6188a10af1791380227f100010001ffff0000000de0b6b3a7640000000000000000000de0b6b3a764000000020001ffff0000003782dace9d900000000000000000003782dace9d90000000030001ffff0000008ac7230489e80000000000000000008ac7230489e8000000040001ffff00000ad78ebc5ac62000000000000000000ad78ebc5ac6200000

-----Decoded View---------------
Arg [0] : catalystRegistry (address): 0xeFa52F2F24A82fA27FAAe3c1eC3cCa52806d1aa7
Arg [1] : sand (address): 0x3845badAde8e6dFF049820680d1F14bD3903a5d0
Arg [2] : asset (address): 0xa342f5D851E866E18ff98F351f2c6637f4478dB5
Arg [3] : gems (address): 0x8Ff2611dA386de427fC96A8073963619c5851Ba5
Arg [4] : metaTx (address): 0x3845badAde8e6dFF049820680d1F14bD3903a5d0
Arg [5] : admin (address): 0xEAA0993E1d21c2103e4f172a20D29371FbAF6D06
Arg [6] : feeCollector (address): 0x0000000000000000000000000000000000000000
Arg [7] : gemAdditionFee (uint256): 1000000000000000000
Arg [8] : catalysts (address): 0x2fC246149B4B8d7bcEF6188A10AF1791380227f1
Arg [9] : bakedInMintdata (uint256[4]): 1766900984260342492861813320019266132353300605199124649858173442675965952,3533748049038726826432794808116932266892205682915777338979779643968585728,5300595113817111163991460283569346020142531941473463759101385845261205504,7067442178595495746128076983446280387693357291614552347222992046553825280

-----Encoded View---------------
13 Constructor Arguments found :
Arg [0] : 000000000000000000000000efa52f2f24a82fa27faae3c1ec3cca52806d1aa7
Arg [1] : 0000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d0
Arg [2] : 000000000000000000000000a342f5d851e866e18ff98f351f2c6637f4478db5
Arg [3] : 0000000000000000000000008ff2611da386de427fc96a8073963619c5851ba5
Arg [4] : 0000000000000000000000003845badade8e6dff049820680d1f14bd3903a5d0
Arg [5] : 000000000000000000000000eaa0993e1d21c2103e4f172a20d29371fbaf6d06
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [8] : 0000000000000000000000002fc246149b4b8d7bcef6188a10af1791380227f1
Arg [9] : 00010001ffff0000000de0b6b3a7640000000000000000000de0b6b3a7640000
Arg [10] : 00020001ffff0000003782dace9d900000000000000000003782dace9d900000
Arg [11] : 00030001ffff0000008ac7230489e80000000000000000008ac7230489e80000
Arg [12] : 00040001ffff00000ad78ebc5ac62000000000000000000ad78ebc5ac6200000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.