Contract Address Details

0x37dfeF9b9c56A81927Dfa73994E2fb23c3dd4b37

Contract Name
Marketplace
Creator
0xef131e–52a472 at 0x3798ed–8330e5
Balance
0 VT
Tokens
Fetching tokens...
Transactions
41 Transactions
Transfers
0 Transfers
Gas Used
3,240,532
Last Balance Update
31618496
Contract name:
Marketplace




Optimization enabled
true
Compiler version
v0.8.4+commit.c7e474f2




Optimization runs
200
EVM Version
istanbul




Verified at
2021-09-14T00:08:58.081944Z

Constructor Arguments

00000000000000000000000084d0c7284a869213cb047595d34d6044d9a7e14a

Arg [0] (address) : 0x84d0c7284a869213cb047595d34d6044d9a7e14a

              

Contract source code

// File: contracts/Identity/IOwned.sol

pragma solidity >0.6.0 <0.9.0;

interface IOwned {
    function owner() external returns (address);
}

// File: contracts/Identity/IOfferable.sol

pragma solidity >=0.8.4 <=0.9.0;

interface IOfferable {
    function offer(address _offeredTo) external;

    function acceptOffer() external;

    function rejectOffer() external;

    function cancelOffer() external;

    function sendTransaction(
        address to,
        bytes memory data,
        uint256 value
    ) external returns (bool success);
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol

pragma solidity ^0.8.0;

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

// File: @openzeppelin/contracts/utils/introspection/ERC165.sol

pragma solidity ^0.8.0;

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override
        returns (bool)
    {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// File: contracts/Identity/OfferableIdentity.sol

pragma solidity >=0.8.4 <=0.9.0;

contract OfferableIdentity is IOfferable, IOwned, ERC165 {
    address public override owner;
    address manager;

    address public offeredTo;

    event TransactionSent(bytes indexed data, uint256 indexed value);

    function init(address _owner) external {
        require(
            manager == address(0),
            "OfferableIdentity: Identity can be initialize only once"
        );
        owner = _owner;
        manager = msg.sender;
        IdentityManager(manager).identityCreated(owner);
    }

    modifier isOwner() {
        require(msg.sender == owner, "OfferableIdentity: Only owner allowed");
        _;
    }

    modifier isManager() {
        require(
            msg.sender == manager,
            "OfferableIdentity: Only manager allowed"
        );
        _;
    }

    modifier isOfferedTo() {
        require(
            offeredTo != address(0),
            "OfferableIdentity: Proxy is not offered"
        );
        require(
            msg.sender == offeredTo,
            "OfferableIdentity: Proxy offered to other account"
        );
        _;
    }

    function offer(address _offeredTo) external override isOwner {
        offeredTo = _offeredTo;
        IdentityManager(manager).identityOffered(offeredTo);
    }

    function acceptOffer() external override isOfferedTo {
        owner = offeredTo;
        IdentityManager(manager).identityAccepted(offeredTo);
        closeOffer();
    }

    function rejectOffer() external override isOfferedTo {
        IdentityManager(manager).identityRejected(offeredTo);
        closeOffer();
    }

    function cancelOffer() external override isOwner {
        IdentityManager(manager).identityOfferCanceled(offeredTo);
        closeOffer();
    }

    function closeOffer() internal {
        offeredTo = address(0);
    }

    function sendTransaction(
        address to,
        bytes memory data,
        uint256 value
    ) external override isOwner returns (bool success) {
        (success, ) = to.call{gas: gasleft() - 5000, value: value}(data);
        require(success, "OfferableIdentity: Error calling other contract");
        emit TransactionSent(data, value);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override
        returns (bool)
    {
        return
            interfaceId == type(IOfferable).interfaceId ||
            interfaceId == type(IOwned).interfaceId ||
            super.supportsInterface(interfaceId);
    }
}

// File: @openzeppelin/contracts/proxy/Clones.sol

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(
                ptr,
                0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
            )
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(
                add(ptr, 0x28),
                0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
            )
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        assembly {
            let ptr := mload(0x40)
            mstore(
                ptr,
                0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
            )
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(
                add(ptr, 0x28),
                0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
            )
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(
                ptr,
                0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
            )
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(
                add(ptr, 0x28),
                0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000
            )
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

// File: contracts/Identity/IdentityManager.sol

pragma solidity >=0.8.4 <=0.9.0;

contract IdentityManager {
    address libraryAddress;

    bytes4 private constant INIT_SELECTOR =
        bytes4(keccak256(bytes("init(address)")));

    struct Identity {
        bool created;
        bool verified;
        bool compliant;
        bool offered;
        address owner;
    }

    mapping(address => Identity) identities;

    event IdentityCreated(
        address indexed identity,
        address indexed owner,
        uint256 indexed at
    );
    event IdentityOffered(
        address indexed identity,
        address indexed owner,
        address offeredTo,
        uint256 indexed at
    );
    event IdentityTransferred(
        address indexed identity,
        address indexed owner,
        uint256 indexed at
    );
    event IdentityOfferRejected(
        address indexed identity,
        address owner,
        address indexed offeredTo,
        uint256 indexed at
    );
    event IdentityOfferCanceled(
        address indexed identity,
        address indexed owner,
        address oferedto,
        uint256 indexed at
    );

    constructor(address _libraryAddress) {
        libraryAddress = _libraryAddress;
    }

    modifier isOfferable() {
        require(
            ERC165Checker.supportsInterface(
                msg.sender,
                type(IOfferable).interfaceId
            ),
            "Only Offerable Identity allowed"
        );
        _;
    }

    modifier isOffered() {
        require(
            identities[msg.sender].offered,
            "IdentityManager: Identity is not offered"
        );
        _;
    }

    function verified(address identity) public view returns (bool) {
        return identities[identity].verified;
    }

    function compliant(address identity) public view returns (bool) {
        return identities[identity].compliant;
    }

    function created(address identity) internal view returns (bool) {
        return identities[identity].created;
    }

    function offered(address identity) internal view returns (bool) {
        return identities[identity].offered;
    }

    function identityOwner(address identity) public view returns (address) {
        return identities[identity].owner;
    }

    function createIdentity(address _owner) external {
        address identity = Clones.clone(libraryAddress);
        identities[identity].created = true;

        bytes memory initData = abi.encodeWithSelector(INIT_SELECTOR, _owner);
        Address.functionCall(
            identity,
            initData,
            "IdentityManager: Can't initialize cloned identity"
        );
    }

    function identityCreated(address _owner) external isOfferable {
        if (created(msg.sender)) {
            identities[msg.sender].verified = true;
        } else {
            identities[msg.sender].compliant = true;
        }
        require(
            identityOwner(msg.sender) == address(0),
            "IdentityManager: Identity already has been registered"
        );
        identities[msg.sender].owner = _owner;
        emit IdentityCreated(msg.sender, _owner, block.timestamp);
    }

    function identityOffered(address _offeredTo) external {
        require(
            verified(msg.sender) || compliant(msg.sender),
            "IdentityManager: Not compliant identity can't be offered"
        );
        identities[msg.sender].offered = true;
        emit IdentityOffered(
            msg.sender,
            identityOwner(msg.sender),
            _offeredTo,
            block.timestamp
        );
    }

    function identityAccepted(address _owner) external isOffered {
        identities[msg.sender].owner = _owner;
        identities[msg.sender].offered = false;
        emit IdentityTransferred(msg.sender, _owner, block.timestamp);
    }

    function identityRejected(address _offeredTo) external isOffered {
        identities[msg.sender].offered = false;
        emit IdentityOfferRejected(
            msg.sender,
            identityOwner(msg.sender),
            _offeredTo,
            block.timestamp
        );
    }

    function identityOfferCanceled(address _offeredTo) external isOffered {
        identities[msg.sender].offered = false;
        emit IdentityOfferCanceled(
            msg.sender,
            identityOwner(msg.sender),
            _offeredTo,
            block.timestamp
        );
    }
}



// File: @openzeppelin/contracts/utils/introspection/ERC165Checker.sol

pragma solidity ^0.8.0;

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface,
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            _supportsERC165Interface(account, type(IERC165).interfaceId) &&
            !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId)
        internal
        view
        returns (bool)
    {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return
            supportsERC165(account) &&
            _supportsERC165Interface(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = _supportsERC165Interface(
                    account,
                    interfaceIds[i]
                );
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in _interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!_supportsERC165Interface(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     * Interface identification is specified in ERC-165.
     */
    function _supportsERC165Interface(address account, bytes4 interfaceId)
        private
        view
        returns (bool)
    {
        bytes memory encodedParams = abi.encodeWithSelector(
            IERC165.supportsInterface.selector,
            interfaceId
        );
        (bool success, bytes memory result) = account.staticcall{gas: 30000}(
            encodedParams
        );
        if (result.length < 32) return false;
        return success && abi.decode(result, (bool));
    }
}

// File: @openzeppelin/contracts/utils/Address.sol

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(
            address(this).balance >= amount,
            "Address: insufficient balance"
        );

        (bool success, ) = recipient.call{value: amount}("");
        require(
            success,
            "Address: unable to send value, recipient may have reverted"
        );
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                "Address: low-level call with value failed"
            );
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(
            address(this).balance >= value,
            "Address: insufficient balance for call"
        );
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(
            data
        );
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data)
        internal
        view
        returns (bytes memory)
    {
        return
            functionStaticCall(
                target,
                data,
                "Address: low-level static call failed"
            );
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return
            functionDelegateCall(
                target,
                data,
                "Address: low-level delegate call failed"
            );
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: contracts/Marketplace/Marketplace.sol

pragma solidity >=0.8.4 <0.9.0;

/**
 * @title Marketplace
 * @dev The Marketplace is a registry for holding offers, demands, and agreements, called matches.
 * @dev Improvements:
 * @dev - Add a cron string for both offers and demands to indicate the timeslot selected.
 * @dev - Create a server to handle and dispatch the requests from the frontend.
 * @dev - Remove most of the data stored on chain to move it to the server or on IPFS.
 * @dev - Define the role of aggregators.
 */
contract Marketplace {
    IdentityManager private _identityManager;

    struct Offer {
        uint256 matches; // number of matches already approved for this offer. An offer can be matched multiple times
        // string cron; // cron schedule of when the asset is able to provide the service
        uint256 volume; // volume provided by the seller in KW
        uint256 remainingVolume; // volume that is still available for the offer
        uint256 price; // price the seller is willing to sell at in ct/KWh
    }

    struct Demand {
        bool isMatched; // wether the demand has been matched or not. A demand can only be matched once
        uint256 volume; // volume provided by the seller in KW
        uint256 price; // price the buyer is willing to buy at in ct/KWh
    }

    struct Match {
        address asset; // address (DID) of the seller
        address buyer; // address (DID) of the buyer
        uint256 volume; // volume requested by the buyer in KW
        uint256 price; // price the seller is willing to sell at in ct/KWh
        bool isAccepted; // wether the match has been accepted or is still pending as a proposal
    }

    uint256 private _currentMatchId = 0;

    // List of all offers in the marketplace by asset (identified by its address/DID)
    mapping(address => Offer) public offers;
    // List of all demands in the marketplace by interested user
    mapping(address => Demand) public demands;
    // List of all matches proposed but not yet accepted by matchId
    mapping(uint256 => Match) public matches;

    /**************************************************************************
     * Events
     **************************************************************************/
    /**
     * @notice A new offer has been added to the marketplace
     * @param asset address (DID) of the asset
     * @param volume volume provided by the seller in KW
     * @param price price the seller is willing to sell at in ct/KWh
     */
    event OfferCreated(address indexed asset, uint256 volume, uint256 price);
    /**
     * @notice A previously issued offer has been cancelled
     * @param asset address (DID) of the asset
     */
    event OfferCancelled(address indexed asset);
    /**
     * @notice A new demand has been added to the marketplace
     * @param buyer address of the buyer
     * @param volume volume requested by the buyer in KW
     * @param price price the buyer is willing to buy at in ct/KWh
     */
    event DemandCreated(address indexed buyer, uint256 volume, uint256 price);
    /**
     * @notice A previously issued demand has been cancelled
     * @param buyer address of the buyer
     */
    event DemandCancelled(address indexed buyer);
    /**
     * @notice A new match has been proposed by the aggregator
     * @param matchId unique identifier of the match
     * @param asset address (DID) of the seller
     * @param buyer address of the buyer
     */
    event MatchProposed(
        uint256 indexed matchId,
        address indexed asset,
        address indexed buyer
    );
    /**
     * @notice A proposed match has been cacelled by the aggregator
     * @param matchId unique identifier of the match
     */
    event MatchCancelled(uint256 indexed matchId);
    /**
     * @notice A proposed match has been accepted by the buyer
     * @param matchId unique identifier of the match
     */
    event MatchAccepted(uint256 indexed matchId);
    /**
     * @notice A proposed match has been rejected by the buyer
     * @param matchId unique identifier of the match
     */
    event MatchRejected(uint256 indexed matchId);
    /**
     * @notice A previously accepted match has been deleted by the buyer or by the asset owner
     * @param matchId unique identifier of the match
     */
    event MatchDeleted(uint256 indexed matchId);

    /**************************************************************************
     * Modifiers
     **************************************************************************/
    /**
     * @notice Make sure both volume and price are greater than 0
     * @param _volume volume provided by the seller in KW
     * @param _price price the seller is willing to sell at in ct/KWh
     */
    modifier validProposal(uint256 _volume, uint256 _price) {
        require(_volume > 0, "Capacity must be greater than 0");
        require(_price > 0, "Price must be greater than 0");
        _;
    }
    /**
     * @notice Make sure the action is carried out by the aggregator
     * todo the _aggregator should be a variable or should be set according to some logic
     * @param _aggregator address of the aggregator
     */
    modifier isAggregator(address _aggregator) {
        require(
            msg.sender == _aggregator,
            "This action can only be carried out by an aggregator"
        );
        _;
    }
    /**
     * @notice Make sure the action is carried out by the asset owner
     * @param _asset address (DID) of the asset
     */
    modifier isAssetOwner(address _asset) {
        require(_asset != address(0), "Asset address cannot be 0x0");
        require(
            msg.sender == _identityManager.identityOwner(_asset),
            "This action can only be carried out by the asset owner"
        );
        _;
    }
    /**
     * @notice Make sure the asset is not already matched
     * @param _asset address (DID) of the asset
     */
    modifier isAssetUnmatched(address _asset) {
        require(
            offers[_asset].matches == 0,
            "This action can only be carried out when the asset is unmatched"
        );
        _;
    }
    /**
     * @notice Make sure the demand is not already matched
     */
    modifier isDemandUnmatched() {
        require(
            !demands[msg.sender].isMatched,
            "This action can only be carried out when the demand is unmatched"
        );
        _;
    }
    /**
     * @notice The match exists
     * @param _matchId unique identifier of the match
     */
    modifier matchExists(uint256 _matchId) {
        require(matches[_matchId].volume > 0, "The match doesn't exists");
        _;
    }
    /**
     * @notice The match has already been accepted by the buyer
     * @param _matchId unique identifier of the match
     */
    modifier isMatchAccepted(uint256 _matchId) {
        require(matches[_matchId].volume > 0, "The match doesn't exists");
        require(
            matches[_matchId].isAccepted,
            "The match must still be accepted"
        );
        _;
    }

    /**************************************************************************
     * Constructor
     **************************************************************************/
    /**
     * @notice Constructor of the Marketplace contract
     * @param _identityManagerAddress address of the IdentityManager contract,
     * used to check the validity of the assets and their owners
     */
    constructor(address _identityManagerAddress) {
        _identityManager = IdentityManager(_identityManagerAddress);
    }

    /**************************************************************************
     * Internal functions
     **************************************************************************/
    /**
     * @notice Updates both the offers and the demands signaling the removal of the match
     * @param _matchId id of the match to remove
     */
    function cleanupAfterMatchRemoval(uint256 _matchId) internal {
        Match memory _match = matches[_matchId];
        offers[_match.asset].remainingVolume += _match.volume;
        offers[_match.asset].matches--;
        demands[_match.buyer].isMatched = false;
        delete matches[_matchId];
    }

    /**************************************************************************
     * External functions
     **************************************************************************/
    /**
     * @notice Create a new offer linked to a specific asset
     * @dev The address that submits the offer must be the owner of the asset
     * @dev The volume must be greater than 0
     * @dev The price must be greater than 0
     * @dev The offed must not be already matched, for it will be overwritten
     * @param _asset address (DID) of the asset
     * @param _volume volume of the asset in KW
     * @param _price price of the energy provided in ct/KWh
     */
    function createOffer(
        address _asset,
        uint256 _volume,
        uint256 _price
    )
        external
        validProposal(_volume, _price)
        isAssetOwner(_asset)
        isAssetUnmatched(_asset)
    {
        offers[_asset] = Offer(0, _volume, _volume, _price);
        emit OfferCreated(_asset, _volume, _price);
    }

    /**
     * @notice Cancels a previously issued offer. Can only be performed if the offer is not matched
     * @dev The address that cancels the offer must be the owner of the asset
     * @dev The offer must exist
     * @dev The offer must not be matched
     * @param _asset address (DID) of the asset
     */
    function cancelOffer(address _asset)
        external
        isAssetOwner(_asset)
        isAssetUnmatched(_asset)
    {
        require(offers[_asset].volume != 0, "Offer does not exist");

        delete offers[_asset];
        emit OfferCancelled(_asset);
    }

    /**
     * @notice Create a new demand
     * @dev The volume must be greater than 0
     * @dev The price must be greater than 0
     * @dev The demand must not be already matched, for it will be overwritten
     * @param _volume volume of the asset in KW
     * @param _price price of the energy provided in ct/KWh
     */
    function createDemand(uint256 _volume, uint256 _price)
        external
        validProposal(_volume, _price)
        isDemandUnmatched
    {
        demands[msg.sender] = Demand(false, _volume, _price);
        emit DemandCreated(msg.sender, _volume, _price);
    }

    /**
     * @notice Cancels a previously issued demand
     * @dev The demand must exist
     * @dev The demand must not be matched
     */
    function cancelDemand() external isDemandUnmatched {
        require(demands[msg.sender].volume != 0, "Demand does not exist");

        delete demands[msg.sender];
        emit DemandCancelled(msg.sender);
    }

    /**
     * @notice Propose a match between an offer and a demand
     * @dev The demand must exist
     * @dev The demand must not be matched
     * @dev The offer must exist
     * @dev The offer must not be matched
     * @dev The volume of the demand must be greater than the volume of the offer
     * @dev The price of the demand must be greater than the price of the offer
     * todo Stronger check to make sure the offer volume is respected from multiple matches
     * @param _asset address (DID) of the asset
     * @param _buyer address of the buyer
     * @param _buyer address of the buyer
     * @param _buyer address of the buyer
     */
    function proposeMatch(
        address _asset,
        address _buyer,
        uint256 _volume,
        uint256 _price
    ) external isAggregator(_buyer) {
        require(offers[_asset].volume > 0, "Offer does not exist");
        require(demands[_buyer].volume > 0, "Demand does not exist");
        require(!demands[_buyer].isMatched, "Demand is already matched");
        require(offers[_asset].price <= _price, "Demand price is too low");
        require(
            offers[_asset].remainingVolume >= _volume,
            "Offer remaining volume is too low"
        );
        _currentMatchId++;
        matches[_currentMatchId] = Match(
            _asset,
            _buyer,
            _volume,
            _price,
            false
        );
        offers[_asset].matches++;
        offers[_asset].remainingVolume -= _volume;
        demands[_buyer].isMatched = true;
        emit MatchProposed(_currentMatchId, _asset, _buyer);
    }

    /**
     * @notice Cancel a previously proposed match
     * @dev Only the aggregator can perform this operation
     * @dev The match must exist
     * @param _matchId id of the match to cancel
     */
    function cancelProposedMatch(uint256 _matchId)
        external
        isAggregator(matches[_matchId].buyer)
        matchExists(_matchId)
    {
        cleanupAfterMatchRemoval(_matchId);
        emit MatchCancelled(_matchId);
    }

    /**
     * @notice Accept a previously proposed match
     * @dev Only the buyer can perform this operation
     * @dev The match must exist
     * @param _matchId id of the match to accept
     */
    function acceptMatch(uint256 _matchId) external matchExists(_matchId) {
        require(
            matches[_matchId].buyer == msg.sender,
            "Only the buyer can accept the match"
        );
        matches[_matchId].isAccepted = true;
        emit MatchAccepted(_matchId);
    }

    /**
     * @notice Reject a previously proposed match
     * @dev Only the buyer can perform this operation
     * @dev The match must exist
     * @param _matchId id of the match to reject
     */
    function rejectMatch(uint256 _matchId) external matchExists(_matchId) {
        require(
            matches[_matchId].buyer == msg.sender ||
                _identityManager.identityOwner(matches[_matchId].asset) ==
                msg.sender,
            "The operation can be performed only by the buyer or the asset owner"
        );
        cleanupAfterMatchRemoval(_matchId);
        emit MatchRejected(_matchId);
    }

    /**
     * @notice Delete a previously accepted match
     * @dev Both the buyer and the asset owner can perform this operation
     * @dev The match must exist and be accepted
     * @param _matchId id of the match to delete
     */
    function deleteMatch(uint256 _matchId) external isMatchAccepted(_matchId) {
        require(
            matches[_matchId].isAccepted,
            "Match must be accepted or rejected first"
        );
        require(
            matches[_matchId].buyer == msg.sender ||
                _identityManager.identityOwner(matches[_matchId].asset) ==
                msg.sender,
            "The operation can be performed only by the buyer or the asset owner"
        );
        cleanupAfterMatchRemoval(_matchId);
        emit MatchDeleted(_matchId);
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_identityManagerAddress","internalType":"address"}]},{"type":"event","name":"DemandCancelled","inputs":[{"type":"address","name":"buyer","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"DemandCreated","inputs":[{"type":"address","name":"buyer","internalType":"address","indexed":true},{"type":"uint256","name":"volume","internalType":"uint256","indexed":false},{"type":"uint256","name":"price","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"MatchAccepted","inputs":[{"type":"uint256","name":"matchId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MatchCancelled","inputs":[{"type":"uint256","name":"matchId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MatchDeleted","inputs":[{"type":"uint256","name":"matchId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MatchProposed","inputs":[{"type":"uint256","name":"matchId","internalType":"uint256","indexed":true},{"type":"address","name":"asset","internalType":"address","indexed":true},{"type":"address","name":"buyer","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"MatchRejected","inputs":[{"type":"uint256","name":"matchId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"OfferCancelled","inputs":[{"type":"address","name":"asset","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OfferCreated","inputs":[{"type":"address","name":"asset","internalType":"address","indexed":true},{"type":"uint256","name":"volume","internalType":"uint256","indexed":false},{"type":"uint256","name":"price","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"acceptMatch","inputs":[{"type":"uint256","name":"_matchId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelDemand","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelOffer","inputs":[{"type":"address","name":"_asset","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelProposedMatch","inputs":[{"type":"uint256","name":"_matchId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createDemand","inputs":[{"type":"uint256","name":"_volume","internalType":"uint256"},{"type":"uint256","name":"_price","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createOffer","inputs":[{"type":"address","name":"_asset","internalType":"address"},{"type":"uint256","name":"_volume","internalType":"uint256"},{"type":"uint256","name":"_price","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deleteMatch","inputs":[{"type":"uint256","name":"_matchId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"isMatched","internalType":"bool"},{"type":"uint256","name":"volume","internalType":"uint256"},{"type":"uint256","name":"price","internalType":"uint256"}],"name":"demands","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"asset","internalType":"address"},{"type":"address","name":"buyer","internalType":"address"},{"type":"uint256","name":"volume","internalType":"uint256"},{"type":"uint256","name":"price","internalType":"uint256"},{"type":"bool","name":"isAccepted","internalType":"bool"}],"name":"matches","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"matches","internalType":"uint256"},{"type":"uint256","name":"volume","internalType":"uint256"},{"type":"uint256","name":"remainingVolume","internalType":"uint256"},{"type":"uint256","name":"price","internalType":"uint256"}],"name":"offers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeMatch","inputs":[{"type":"address","name":"_asset","internalType":"address"},{"type":"address","name":"_buyer","internalType":"address"},{"type":"uint256","name":"_volume","internalType":"uint256"},{"type":"uint256","name":"_price","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rejectMatch","inputs":[{"type":"uint256","name":"_matchId","internalType":"uint256"}]}]
            

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106100b35760003560e01c80636353d0ce116100715780636353d0ce146101e45780639512ada2146101ec5780639b8a74f0146101ff578063bebcd21e14610212578063dd29757a14610225578063f11610231461027557600080fd5b8062fbf79d146100b85780631b6b2601146100cd5780631c6c7128146100e0578063220ec9d2146100f3578063413bf38f146101065780634768d4ef14610162575b600080fd5b6100cb6100c63660046112ea565b610288565b005b6100cb6100db36600461137b565b610617565b6100cb6100ee36600461132f565b61076e565b6100cb610101366004611363565b6109e5565b61013d6101143660046112ab565b600260208190526000918252604090912080546001820154928201546003909201549092919084565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b6101af610170366004611363565b6004602081905260009182526040909120805460018201546002830154600384015493909401546001600160a01b039283169492909116929060ff1685565b604080516001600160a01b0396871681529590941660208601529284019190915260608301521515608082015260a001610159565b6100cb610a88565b6100cb6101fa366004611363565b610b5b565b6100cb61020d3660046112ab565b610d74565b6100cb610220366004611363565b610f6c565b6102586102333660046112ab565b60036020526000908152604090208054600182015460029092015460ff909116919083565b604080519315158452602084019290925290820152606001610159565b6100cb610283366004611363565b61105a565b82336001600160a01b038216146102ba5760405162461bcd60e51b81526004016102b190611405565b60405180910390fd5b6001600160a01b0385166000908152600260205260409020600101546103195760405162461bcd60e51b815260206004820152601460248201527313d999995c88191bd95cc81b9bdd08195e1a5cdd60621b60448201526064016102b1565b6001600160a01b0384166000908152600360205260409020600101546103795760405162461bcd60e51b815260206004820152601560248201527411195b585b9908191bd95cc81b9bdd08195e1a5cdd605a1b60448201526064016102b1565b6001600160a01b03841660009081526003602052604090205460ff16156103e25760405162461bcd60e51b815260206004820152601960248201527f44656d616e6420697320616c7265616479206d6174636865640000000000000060448201526064016102b1565b6001600160a01b03851660009081526002602052604090206003015482101561044d5760405162461bcd60e51b815260206004820152601760248201527f44656d616e6420707269636520697320746f6f206c6f7700000000000000000060448201526064016102b1565b6001600160a01b038516600090815260026020819052604090912001548311156104c35760405162461bcd60e51b815260206004820152602160248201527f4f666665722072656d61696e696e6720766f6c756d6520697320746f6f206c6f6044820152607760f81b60648201526084016102b1565b600180549060006104d38361159f565b90915550506040805160a0810182526001600160a01b0380881680835287821660208085019182528486018981526060860189815260006080880181815260018054835260048087528b84209a518b54908b166001600160a01b0319918216178c559751918b01805492909a169190971617909755915160028089019190915590516003880155945195909201805495151560ff199096169590951790945590815291529081208054916105868361159f565b90915550506001600160a01b038516600090815260026020819052604082200180548592906105b6908490611571565b90915550506001600160a01b03808516600081815260036020526040808220805460ff19166001908117909155549051929389169290917fe4d8e0f7da725faec426061c134daa691fa7ab36a63d5a70093e1408a3b7006d91a45050505050565b8181600082116106695760405162461bcd60e51b815260206004820152601f60248201527f4361706163697479206d7573742062652067726561746572207468616e20300060448201526064016102b1565b600081116106b95760405162461bcd60e51b815260206004820152601c60248201527f5072696365206d7573742062652067726561746572207468616e20300000000060448201526064016102b1565b3360009081526003602052604090205460ff16156106e95760405162461bcd60e51b81526004016102b19061150d565b60408051606081018252600080825260208083018881528385018881523380855260038452938690209451855460ff191690151517855590516001850155516002909301929092558251878152918201869052917fc87629af897fb7ab00e4fc3e1f1a7979fc163449b23c2c82f5ff24f1380766ab910160405180910390a250505050565b8181600082116107c05760405162461bcd60e51b815260206004820152601f60248201527f4361706163697479206d7573742062652067726561746572207468616e20300060448201526064016102b1565b600081116108105760405162461bcd60e51b815260206004820152601c60248201527f5072696365206d7573742062652067726561746572207468616e20300000000060448201526064016102b1565b846001600160a01b0381166108675760405162461bcd60e51b815260206004820152601b60248201527f417373657420616464726573732063616e6e6f7420626520307830000000000060448201526064016102b1565b6000546040516310e67a9d60e31b81526001600160a01b03838116600483015290911690638733d4e89060240160206040518083038186803b1580156108ac57600080fd5b505afa1580156108c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e491906112ce565b6001600160a01b0316336001600160a01b0316146109145760405162461bcd60e51b81526004016102b1906114c9565b6001600160a01b03861660009081526002602052604090205486901561094c5760405162461bcd60e51b81526004016102b190611447565b60408051608081018252600080825260208083018a81528385018b8152606085018b81526001600160a01b038e16808652600280865295889020965187559251600187015590519385019390935591516003909301929092558251898152918201889052917fe899a6e29fab942958a400b4db4c5dec136446a7966a69e0011735027a33fff3910160405180910390a250505050505050565b6000818152600460205260409020600101546001600160a01b0316338114610a1f5760405162461bcd60e51b81526004016102b190611405565b6000828152600460205260409020600201548290610a4f5760405162461bcd60e51b81526004016102b190611492565b610a588361119e565b60405183907f700135b4fe8746e2d2c85a9baa43c62887740aebfeb3a439f71a083fe5d5675990600090a2505050565b3360009081526003602052604090205460ff1615610ab85760405162461bcd60e51b81526004016102b19061150d565b33600090815260036020526040902060010154610b0f5760405162461bcd60e51b815260206004820152601560248201527411195b585b9908191bd95cc81b9bdd08195e1a5cdd605a1b60448201526064016102b1565b33600081815260036020526040808220805460ff1916815560018101839055600201829055517fbb0484f4cc4b64d8fcffeafa625bb8a5513907608f51d2f670592eabc26df8699190a2565b6000818152600460205260409020600201548190610b8b5760405162461bcd60e51b81526004016102b190611492565b6000818152600460208190526040909120015460ff16610bed5760405162461bcd60e51b815260206004820181905260248201527f546865206d61746368206d757374207374696c6c20626520616363657074656460448201526064016102b1565b6000828152600460208190526040909120015460ff16610c605760405162461bcd60e51b815260206004820152602860248201527f4d61746368206d757374206265206163636570746564206f722072656a656374604482015267195908199a5c9cdd60c21b60648201526084016102b1565b6000828152600460205260409020600101546001600160a01b0316331480610d20575060008054838252600460208190526040928390205492516310e67a9d60e31b81526001600160a01b0393841691810191909152339290911690638733d4e89060240160206040518083038186803b158015610cdd57600080fd5b505afa158015610cf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1591906112ce565b6001600160a01b0316145b610d3c5760405162461bcd60e51b81526004016102b19061139c565b610d458261119e565b60405182907f4a5a134b29f0b3201b6013517626460a42e26046e6827189dc25ed7de0a87b6390600090a25050565b806001600160a01b038116610dcb5760405162461bcd60e51b815260206004820152601b60248201527f417373657420616464726573732063616e6e6f7420626520307830000000000060448201526064016102b1565b6000546040516310e67a9d60e31b81526001600160a01b03838116600483015290911690638733d4e89060240160206040518083038186803b158015610e1057600080fd5b505afa158015610e24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e4891906112ce565b6001600160a01b0316336001600160a01b031614610e785760405162461bcd60e51b81526004016102b1906114c9565b6001600160a01b038216600090815260026020526040902054829015610eb05760405162461bcd60e51b81526004016102b190611447565b6001600160a01b038316600090815260026020526040902060010154610f0f5760405162461bcd60e51b815260206004820152601460248201527313d999995c88191bd95cc81b9bdd08195e1a5cdd60621b60448201526064016102b1565b6001600160a01b0383166000818152600260208190526040808320838155600181018490559182018390556003909101829055517f58c450e79e079445cb4236ae6f118053d819a6d6477d4b4e91b3552a8ef00fc89190a2505050565b6000818152600460205260409020600201548190610f9c5760405162461bcd60e51b81526004016102b190611492565b6000828152600460205260409020600101546001600160a01b031633146110115760405162461bcd60e51b815260206004820152602360248201527f4f6e6c79207468652062757965722063616e2061636365707420746865206d616044820152620e8c6d60eb1b60648201526084016102b1565b6000828152600460208190526040808320909101805460ff191660011790555183917f73715571185459f4f39409a64e51c86a3d41bb71cf95d02554c190501fdf948891a25050565b600081815260046020526040902060020154819061108a5760405162461bcd60e51b81526004016102b190611492565b6000828152600460205260409020600101546001600160a01b031633148061114a575060008054838252600460208190526040928390205492516310e67a9d60e31b81526001600160a01b0393841691810191909152339290911690638733d4e89060240160206040518083038186803b15801561110757600080fd5b505afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f91906112ce565b6001600160a01b0316145b6111665760405162461bcd60e51b81526004016102b19061139c565b61116f8261119e565b60405182907f745319c15fac54211db76094086fd5c643ecd171d1ce911b754e852944b6f2e490600090a25050565b6000818152600460208181526040808420815160a08101835281546001600160a01b0390811680835260018401549091168286015260028084015483860181905260038501546060850152939096015460ff1615156080830152865292849052908420909201805491939091611215908490611559565b909155505080516001600160a01b0316600090815260026020526040812080549161123f83611588565b90915550506020908101516001600160a01b0316600090815260038083526040808320805460ff19908116909155948352600493849052822080546001600160a01b03199081168255600182018054909116905560028101839055908101919091550180549091169055565b6000602082840312156112bc578081fd5b81356112c7816115d0565b9392505050565b6000602082840312156112df578081fd5b81516112c7816115d0565b600080600080608085870312156112ff578283fd5b843561130a816115d0565b9350602085013561131a816115d0565b93969395505050506040820135916060013590565b600080600060608486031215611343578283fd5b833561134e816115d0565b95602085013595506040909401359392505050565b600060208284031215611374578081fd5b5035919050565b6000806040838503121561138d578182fd5b50508035926020909101359150565b60208082526043908201527f546865206f7065726174696f6e2063616e20626520706572666f726d6564206f60408201527f6e6c7920627920746865206275796572206f7220746865206173736574206f776060820152623732b960e91b608082015260a00190565b60208082526034908201526000805160206115e983398151915260408201527337baba10313c9030b71030b3b3b932b3b0ba37b960611b606082015260800190565b6020808252603f908201526000805160206115e983398151915260408201527f6f7574207768656e2074686520617373657420697320756e6d61746368656400606082015260800190565b60208082526018908201527f546865206d6174636820646f65736e2774206578697374730000000000000000604082015260600190565b60208082526036908201526000805160206115e983398151915260408201527537baba10313c903a34329030b9b9b2ba1037bbb732b960511b606082015260800190565b602080825260409082018190526000805160206115e9833981519152908201527f6f7574207768656e207468652064656d616e6420697320756e6d617463686564606082015260800190565b6000821982111561156c5761156c6115ba565b500190565b600082821015611583576115836115ba565b500390565b600081611597576115976115ba565b506000190190565b60006000198214156115b3576115b36115ba565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03811681146115e557600080fd5b5056fe5468697320616374696f6e2063616e206f6e6c79206265206361727269656420a2646970667358221220f1579a35f81940d705f9be09fcf172caf73adfe3ffefd8b9f7f32aceba2af0f264736f6c63430008040033