Change ZeroAddress() to CannotUpgrade()

Change ZeroAddress() to CannotUpgrade()

Co-authored-by: Nick Johnson <b4b46ede95e0e5ce2a65a83d421c9acf68db8288@notdot.net>

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./ERC1155Fuse.sol";
import "./Controllable.sol";
import "./INameWrapper.sol";
import "./INameWrapperUpgrade.sol";
import "./IMetadataService.sol";
import "../registry/ENS.sol";
import "../ethregistrar/IBaseRegistrar.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./BytesUtil.sol";

error Unauthorised(bytes32 node, address addr);
error NameNotFound();
error IncompatibleParent();
error IncompatibleName(bytes name);
error IncorrectTokenType();
error LabelMismatch(bytes32 labelHash, bytes32 expectedLabelhash);
error LabelTooShort();
error LabelTooLong(string label);
error IncorrectTargetOwner(address owner);
error CannotUpgrade();

contract NameWrapper is
    Ownable,
    ERC1155Fuse,
    INameWrapper,
    Controllable,
    IERC721Receiver
{
    using BytesUtils for bytes;
    ENS public immutable override ens;
    IBaseRegistrar public immutable override registrar;
    IMetadataService public override metadataService;
    mapping(bytes32 => bytes) public override names;

    bytes32 private constant ETH_NODE =
        0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;
    bytes32 private constant ROOT_NODE =
        0x0000000000000000000000000000000000000000000000000000000000000000;

    //A contract address to a new upgraded contract if any
    INameWrapperUpgrade public upgradeContract;

    constructor(
        ENS _ens,
        IBaseRegistrar _registrar,
        IMetadataService _metadataService
    ) {
        ens = _ens;
        registrar = _registrar;
        metadataService = _metadataService;

        /* Burn CANNOT_REPLACE_SUBDOMAIN and CANNOT_UNWRAP fuses for ROOT_NODE and ETH_NODE */

        _setData(
            uint256(ETH_NODE),
            address(0x0),
            uint96(CANNOT_REPLACE_SUBDOMAIN | CANNOT_UNWRAP)
        );
        _setData(
            uint256(ROOT_NODE),
            address(0x0),
            uint96(CANNOT_REPLACE_SUBDOMAIN | CANNOT_UNWRAP)
        );
        names[ROOT_NODE] = "\x00";
        names[ETH_NODE] = "\x03eth\x00";
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC1155Fuse, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(INameWrapper).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /* Metadata service */

    /**
     * @notice Set the metadata service. only admin can do this
     */

    function setMetadataService(IMetadataService _newMetadataService)
        public
        onlyOwner
    {
        metadataService = _newMetadataService;
    }

    /**
     * @notice Get the metadata uri
     * @return String uri of the metadata service
     */

    function uri(uint256 tokenId) public view override returns (string memory) {
        return metadataService.uri(tokenId);
    }

    /**
     * @notice Set the address of the upgradeContract of the contract. only admin can do this
     * @dev The default value of upgradeContract is the 0 address. Use the 0 address at any time
     * to make the contract not upgradable.
     * @param _upgradeAddress address of an upgraded contract
     */

    function setUpgradeContract(INameWrapperUpgrade _upgradeAddress)
        public
        onlyOwner
    {
            if (address(upgradeContract) != address(0)){
                registrar.setApprovalForAll(address(upgradeContract), false);
                ens.setApprovalForAll(address(upgradeContract), false);
            }

            upgradeContract = _upgradeAddress;

            if (address(upgradeContract) != address(0)){
                registrar.setApprovalForAll(address(upgradeContract), true);
                ens.setApprovalForAll(address(upgradeContract), true);
            }
    }

    /**
     * @notice Checks if msg.sender is the owner or approved by the owner of a name
     * @param node namehash of the name to check
     */

    modifier onlyTokenOwner(bytes32 node) {
        if (!isTokenOwnerOrApproved(node, msg.sender)) {
            revert Unauthorised(node, msg.sender);
        }

        _;
    }

    /**
     * @notice Checks if owner or approved by owner
     * @param node namehash of the name to check
     * @param addr which address to check permissions for
     * @return whether or not is owner or approved
     */

    function isTokenOwnerOrApproved(bytes32 node, address addr)
        public
        view
        override
        returns (bool)
    {
        address owner = ownerOf(uint256(node));
        return owner == addr || isApprovedForAll(owner, addr);
    }

    /**
     * @notice Gets fuse permissions for a specific name
     * @dev Fuses are represented by a uint96 where each permission is represented by 1 bit
     *      The interface has predefined fuses for all registry permissions, but additional
     *      fuses can be added for other use cases
     * @param node namehash of the name to check
     * @return fuses A number that represents the permissions a name has
     * @return vulnerability The type of vulnerability
     * @return vulnerableNode Which node is vulnerable
     */
    function getFuses(bytes32 node)
        public
        view
        override
        returns (
            uint96 fuses,
            NameSafety vulnerability,
            bytes32 vulnerableNode
        )
    {
        bytes memory name = names[node];
        if (name.length < 1) {
            revert NameNotFound();
        }
        (, vulnerability, vulnerableNode) = _checkHierarchy(name, 0);
        (, fuses) = getData(uint256(node));
    }

    /**
     * @notice Wraps a .eth domain, creating a new token and sending the original ERC721 token to this *         contract
     * @dev Can be called by the owner of the name in the .eth registrar or an authorised caller on the *      registrar
     * @param label label as a string of the .eth domain to wrap
     * @param _fuses initial fuses to set
     * @param wrappedOwner Owner of the name in this contract
     */

    function wrapETH2LD(
        string calldata label,
        address wrappedOwner,
        uint96 _fuses,
        address resolver
    ) public override {
        uint256 tokenId = uint256(keccak256(bytes(label)));
        address registrant = registrar.ownerOf(tokenId);
        if (
            registrant != msg.sender &&
            !isApprovedForAll(registrant, msg.sender) &&
            !registrar.isApprovedForAll(registrant, msg.sender)
        ) {
            revert Unauthorised(
                _makeNode(ETH_NODE, bytes32(tokenId)),
                msg.sender
            );
        }

        // transfer the token from the user to this contract
        registrar.transferFrom(registrant, address(this), tokenId);

        // transfer the ens record back to the new owner (this contract)
        registrar.reclaim(tokenId, address(this));

        _wrapETH2LD(label, wrappedOwner, _fuses, resolver);
    }

    /**
     * @dev Registers a new .eth second-level domain and wraps it.
     *      Only callable by authorised controllers.
     * @param label The label to register (Eg, 'foo' for 'foo.eth').
     * @param wrappedOwner The owner of the wrapped name.
     * @param duration The duration, in seconds, to register the name for.
     * @param resolver The resolver address to set on the ENS registry (optional).
     * @return expires The expiry date of the new name, in seconds since the Unix epoch.
     */
    function registerAndWrapETH2LD(
        string calldata label,
        address wrappedOwner,
        uint256 duration,
        address resolver,
        uint96 _fuses
    ) external override onlyController returns (uint256 expires) {
        uint256 tokenId = uint256(keccak256(bytes(label)));

        expires = registrar.register(tokenId, address(this), duration);
        _wrapETH2LD(label, wrappedOwner, _fuses, resolver);
    }

    /**
     * @dev Renews a .eth second-level domain.
     *      Only callable by authorised controllers.
     * @param tokenId The hash of the label to register (eg, `keccak256('foo')`, for 'foo.eth').
     * @param duration The number of seconds to renew the name for.
     * @return expires The expiry date of the name, in seconds since the Unix epoch.
     */
    function renew(uint256 tokenId, uint256 duration)
        external
        override
        onlyController
        returns (uint256 expires)
    {
        return registrar.renew(tokenId, duration);
    }

    /**
     * @notice Wraps a non .eth domain, of any kind. Could be a DNSSEC name vitalik.xyz or a subdomain
     * @dev Can be called by the owner in the registry or an authorised caller in the registry
     * @param name The name to wrap, in DNS format
     * @param _fuses initial fuses to set represented as a number. Check getFuses() for more info
     * @param wrappedOwner Owner of the name in this contract
     */

    function wrap(
        bytes calldata name,
        address wrappedOwner,
        uint96 _fuses,
        address resolver
    ) public override {
        (bytes32 labelhash, uint256 offset) = name.readLabel(0);
        bytes32 parentNode = name.namehash(offset);
        bytes32 node = _makeNode(parentNode, labelhash);

        if (parentNode == ETH_NODE) {
            revert IncompatibleParent();
        }

        address owner = ens.owner(node);

        if (
            owner != msg.sender &&
            !isApprovedForAll(owner, msg.sender) &&
            !ens.isApprovedForAll(owner, msg.sender)
        ) {
            revert Unauthorised(node, msg.sender);
        }

        if (resolver != address(0)) {
            ens.setResolver(node, resolver);
        }

        ens.setOwner(node, address(this));

        _wrap(node, name, wrappedOwner, _fuses);
    }

    /**
     * @notice Unwraps a .eth domain. e.g. vitalik.eth
     * @dev Can be called by the owner in the wrapper or an authorised caller in the wrapper
     * @param labelhash labelhash of the .eth domain
     * @param newRegistrant sets the owner in the .eth registrar to this address
     * @param newController sets the owner in the registry to this address
     */

    function unwrapETH2LD(
        bytes32 labelhash,
        address newRegistrant,
        address newController
    ) public override onlyTokenOwner(_makeNode(ETH_NODE, labelhash)) {
        _unwrap(_makeNode(ETH_NODE, labelhash), newController);
        registrar.transferFrom(
            address(this),
            newRegistrant,
            uint256(labelhash)
        );
    }

    /**
     * @notice Unwraps a non .eth domain, of any kind. Could be a DNSSEC name vitalik.xyz or a subdomain
     * @dev Can be called by the owner in the wrapper or an authorised caller in the wrapper
     * @param parentNode parent namehash of the name to wrap e.g. vitalik.xyz would be namehash('xyz')
     * @param labelhash labelhash of the .eth domain
     * @param newController sets the owner in the registry to this address
     */

    function unwrap(
        bytes32 parentNode,
        bytes32 labelhash,
        address newController
    ) public override onlyTokenOwner(_makeNode(parentNode, labelhash)) {
        if (parentNode == ETH_NODE) {
            revert IncompatibleParent();
        }
        _unwrap(_makeNode(parentNode, labelhash), newController);
    }

    /**
     * @notice Burns any fuse passed to this function for a name
     * @dev Fuse burns are always additive and will not unburn already burnt fuses
     * @param node namehash of the name. e.g. vitalik.xyz would be namehash('vitalik.xyz')
     * @param _fuses Fuses you want to burn.
     */

    function burnFuses(bytes32 node, uint96 _fuses)
        public
        override
        onlyTokenOwner(node)
        operationAllowed(node, CANNOT_BURN_FUSES)
    {
        (address owner, uint96 fuses) = getData(uint256(node));

        uint96 newFuses = fuses | _fuses;

        _setData(uint256(node), owner, newFuses);

        emit FusesBurned(node, newFuses);
    }

    /**
     * @notice Upgrades a .eth wrapped domain by calling the wrapETH2LD function of the upgradeAddress
     *  contract and burning the token of this contract.
     * @dev Can be called by the owner of the name in this contract
     * @param label label as a string of the .eth domain to upgrade
     * @param wrappedOwner The owner of the wrapped name.
     */

    function upgradeETH2LD(
        string calldata label,
        address wrappedOwner
    )
        public
    {

        if (address(upgradeContract) == address(0)){
            revert ZeroAddress();
        }

        bytes32 labelhash = keccak256(bytes(label));
        bytes32 node = _makeNode(ETH_NODE, labelhash);

        if (!isTokenOwnerOrApproved(node, msg.sender)){
            revert Unauthorised(node, msg.sender);
        }

        (uint96 fuses,,) = getFuses(node);

        address resolver = ens.resolver(node);

        upgradeContract.wrapETH2LD(label, wrappedOwner, fuses, resolver);

        // burn token and fuse data
        _burn(uint256(node));

    }

    /**
     * @notice Upgrades a non .eth domain of any kind. Could be a DNSSEC name vitalik.xyz or a subdomain
     * @dev Can be called by the owner or an authorised caller
     * @param name The name to upgrade, in DNS format
     * @param wrappedOwner owner of the name in this contract
     */

    function upgrade(
        bytes calldata name,
        address wrappedOwner
    ) public {

        if (address(upgradeContract) == address(0)){
            revert ZeroAddress();
        }

        (bytes32 labelhash, uint offset) = name.readLabel(0);
        bytes32 parentNode = name.namehash(offset);
        bytes32 node = _makeNode(parentNode, labelhash);

        if (!isTokenOwnerOrApproved(node, msg.sender)){
            revert Unauthorised(node, msg.sender);
        }

        address resolver = ens.resolver(node);

        (uint96 fuses,,) = getFuses(node);

        upgradeContract.wrap(name, wrappedOwner, fuses, resolver);

        // burn token and fuse data
        _burn(uint256(node));
    }

    /**
     * @notice Sets records for the subdomain in the ENS Registry
     * @param parentNode namehash of the parent name
     * @param labelhash labelhash of the subnode
     * @param owner newOwner in the registry
     * @param resolver the resolver contract in the registry
     * @param ttl ttl in the registry
     */

    function setSubnodeRecord(
        bytes32 parentNode,
        bytes32 labelhash,
        address owner,
        address resolver,
        uint64 ttl
    )
        public
        override
        onlyTokenOwner(parentNode)
        canCallSetSubnodeOwner(parentNode, labelhash)
    {
        ens.setSubnodeRecord(parentNode, labelhash, owner, resolver, ttl);
    }

    /**
     * @notice Sets the subnode owner in the registry
     * @param parentNode namehash of the parent name
     * @param labelhash labelhash of the subnode
     * @param owner newOwner in the registry
     */

    function setSubnodeOwner(
        bytes32 parentNode,
        bytes32 labelhash,
        address owner
    )
        public
        override
        onlyTokenOwner(parentNode)
        canCallSetSubnodeOwner(parentNode, labelhash)
        returns (bytes32)
    {
        return ens.setSubnodeOwner(parentNode, labelhash, owner);
    }

    /**
     * @notice Sets the subdomain owner in the registry and then wraps the subdomain
     * @param parentNode parent namehash of the subdomain
     * @param label label of the subdomain as a string
     * @param newOwner newOwner in the registry
     * @param _fuses initial fuses for the wrapped subdomain
     */

    function setSubnodeOwnerAndWrap(
        bytes32 parentNode,
        string calldata label,
        address newOwner,
        uint96 _fuses
    ) public override returns (bytes32 node) {
        bytes32 labelhash = keccak256(bytes(label));
        node = _makeNode(parentNode, labelhash);
        bytes memory name = _addLabel(label, names[parentNode]);

        setSubnodeOwner(parentNode, labelhash, address(this));

        _wrap(node, name, newOwner, _fuses);
    }

    /**
     * @notice Sets the subdomain owner in the registry with records and then wraps the subdomain
     * @param parentNode parent namehash of the subdomain
     * @param label label of the subdomain as a string
     * @param newOwner newOwner in the registry
     * @param resolver resolver contract in the registry
     * @param ttl ttl in the regsitry
     * @param _fuses initial fuses for the wrapped subdomain
     */

    function setSubnodeRecordAndWrap(
        bytes32 parentNode,
        string calldata label,
        address newOwner,
        address resolver,
        uint64 ttl,
        uint96 _fuses
    ) public override {
        bytes32 labelhash = keccak256(bytes(label));
        bytes32 node = _makeNode(parentNode, labelhash);
        bytes memory name = _addLabel(label, names[parentNode]);

        setSubnodeRecord(parentNode, labelhash, address(this), resolver, ttl);

        _wrap(node, name, newOwner, _fuses);
    }

    /**
     * @notice Sets records for the name in the ENS Registry
     * @param node namehash of the name to set a record for
     * @param owner newOwner in the registry
     * @param resolver the resolver contract
     * @param ttl ttl in the registry
     */

    function setRecord(
        bytes32 node,
        address owner,
        address resolver,
        uint64 ttl
    )
        public
        override
        onlyTokenOwner(node)
        operationAllowed(
            node,
            CANNOT_TRANSFER | CANNOT_SET_RESOLVER | CANNOT_SET_TTL
        )
    {
        ens.setRecord(node, owner, resolver, ttl);
    }

    /**
     * @notice Sets resolver contract in the registry
     * @param node namehash of the name
     * @param resolver the resolver contract
     */

    function setResolver(bytes32 node, address resolver)
        public
        override
        onlyTokenOwner(node)
        operationAllowed(node, CANNOT_SET_RESOLVER)
    {
        ens.setResolver(node, resolver);
    }

    /**
     * @notice Sets TTL in the registry
     * @param node namehash of the name
     * @param ttl TTL in the registry
     */

    function setTTL(bytes32 node, uint64 ttl)
        public
        override
        onlyTokenOwner(node)
        operationAllowed(node, CANNOT_SET_TTL)
    {
        ens.setTTL(node, ttl);
    }

    /**
     * @dev Allows an operation only if none of the specified fuses are burned.
     * @param node The namehash of the name to check fuses on.
     * @param fuseMask A bitmask of fuses that must not be burned.
     */
    modifier operationAllowed(bytes32 node, uint96 fuseMask) {
        (, uint96 fuses) = getData(uint256(node));
        if (fuses & fuseMask != 0) {
            revert OperationProhibited(node);
        }
        _;
    }

    /**
     * @notice Check whether a name can call setSubnodeOwner/setSubnodeRecord
     * @dev Checks both canCreateSubdomain and canReplaceSubdomain and whether not they have been burnt
     *      and checks whether the owner of the subdomain is 0x0 for creating or already exists for
     *      replacing a subdomain. If either conditions are true, then it is possible to call
     *      setSubnodeOwner
     * @param node namehash of the name to check
     * @param labelhash labelhash of the name to check
     */

    modifier canCallSetSubnodeOwner(bytes32 node, bytes32 labelhash) {
        bytes32 subnode = _makeNode(node, labelhash);
        address owner = ens.owner(subnode);
        (, uint96 fuses) = getData(uint256(node));

        if (
            (owner == address(0) && fuses & CANNOT_CREATE_SUBDOMAIN != 0) ||
            (owner != address(0) && fuses & CANNOT_REPLACE_SUBDOMAIN != 0)
        ) {
            revert OperationProhibited(node);
        }

        _;
    }

    /**
     * @notice Checks all Fuses in the mask are burned for the node
     * @param node namehash of the name
     * @param fuseMask the fuses you want to check
     * @return Boolean of whether or not all the selected fuses are burned
     */

    function allFusesBurned(bytes32 node, uint96 fuseMask)
        public
        view
        override
        returns (bool)
    {
        (, uint96 fuses) = getData(uint256(node));
        return fuses & fuseMask == fuseMask;
    }

    function onERC721Received(
        address to,
        address,
        uint256 tokenId,
        bytes calldata data
    ) public override returns (bytes4) {
        //check if it's the eth registrar ERC721
        if (msg.sender != address(registrar)) {
            revert IncorrectTokenType();
        }

        (
            string memory label,
            address owner,
            uint96 fuses,
            address resolver
        ) = abi.decode(data, (string, address, uint96, address));

        bytes32 labelhash = bytes32(tokenId);
        bytes32 labelhashFromData = keccak256(bytes(label));

        if (labelhashFromData != labelhash) {
            revert LabelMismatch(labelhashFromData, labelhash);
        }

        // transfer the ens record back to the new owner (this contract)
        registrar.reclaim(uint256(labelhash), address(this));

        _wrapETH2LD(label, owner, fuses, resolver);

        return IERC721Receiver(to).onERC721Received.selector;
    }

    /***** Internal functions */

    function _canTransfer(uint96 fuses) internal pure override returns (bool) {
        return fuses & CANNOT_TRANSFER == 0;
    }

    function _makeNode(bytes32 node, bytes32 labelhash)
        private
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(node, labelhash));
    }

    function _addLabel(string memory label, bytes memory name)
        internal
        pure
        returns (bytes memory ret)
    {
        if (bytes(label).length < 1) {
            revert LabelTooShort();
        }
        if (bytes(label).length > 255) {
            revert LabelTooLong(label);
        }
        return abi.encodePacked(uint8(bytes(label).length), label, name);
    }

    function _mint(
        bytes32 node,
        address wrappedOwner,
        uint96 _fuses
    ) internal override {
        address oldWrappedOwner = ownerOf(uint256(node));
        if (oldWrappedOwner != address(0)) {
            // burn and unwrap old token of old owner
            _burn(uint256(node));
            emit NameUnwrapped(node, address(0));
        }
        super._mint(node, wrappedOwner, _fuses);
    }

    function _wrap(
        bytes32 node,
        bytes memory name,
        address wrappedOwner,
        uint96 fuses
    ) internal {
        names[node] = name;

        _mint(node, wrappedOwner, fuses);

        emit NameWrapped(node, name, wrappedOwner, fuses);
    }

    function _wrapETH2LD(
        string memory label,
        address wrappedOwner,
        uint96 _fuses,
        address resolver
    ) private returns (bytes32 labelhash) {
        labelhash = keccak256(bytes(label));
        bytes32 node = _makeNode(ETH_NODE, labelhash);
        bytes memory name = _addLabel(label, "\x03eth\x00");

        if (resolver != address(0)) {
            ens.setResolver(node, resolver);
        }

        // mint a new ERC1155 token with fuses
        _wrap(node, name, wrappedOwner, _fuses);
    }

    function _unwrap(bytes32 node, address newOwner) private {
        if (newOwner == address(0x0) || newOwner == address(this)) {
            revert IncorrectTargetOwner(newOwner);
        }

        if (allFusesBurned(node, CANNOT_UNWRAP)) {
            revert OperationProhibited(node);
        }

        // burn token and fuse data
        _burn(uint256(node));
        ens.setOwner(node, newOwner);

        emit NameUnwrapped(node, newOwner);
    }

    function _setData(
        uint256 tokenId,
        address owner,
        uint96 fuses
    ) internal override {
        if (fuses != CAN_DO_EVERYTHING && fuses & CANNOT_UNWRAP == 0) {
            revert OperationProhibited(bytes32(tokenId));
        }
        super._setData(tokenId, owner, fuses);
    }

    /**
     * @dev Internal function that checks all a name's ancestors to ensure fuse values will be respected and parent controller/registrant are set to the Wrapper
     * @param name The name to check.
     * @param offset The offset into the name to start at.
     * @return node The calculated namehash for this part of the name.
     * @return vulnerability what kind of vulnerability the node has
     * @return vulnerableNode which node is at risk
     */
    function _checkHierarchy(bytes memory name, uint256 offset)
        internal
        view
        returns (
            bytes32 node,
            NameSafety vulnerability,
            bytes32 vulnerableNode
        )
    {
        // Read the first label. If it's the root, return immediately.
        (bytes32 labelhash, uint256 newOffset) = name.readLabel(offset);
        if (labelhash == bytes32(0)) {
            // Root node
            return (bytes32(0), NameSafety.Safe, 0);
        }

        // Check the parent name
        bytes32 parentNode;
        (parentNode, vulnerability, vulnerableNode) = _checkHierarchy(
            name,
            newOffset
        );

        node = _makeNode(parentNode, labelhash);

        // stop function checking any other nodes if a parent is not safe
        if (vulnerability != NameSafety.Safe) {
            return (node, vulnerability, vulnerableNode);
        }

        // Check the parent name's fuses to see if replacing subdomains is forbidden
        if (parentNode == ROOT_NODE) {
            // Save ourselves some gas; root node can't be replaced
            return (node, NameSafety.Safe, 0);
        }

        (vulnerability, vulnerableNode) = _checkOwnership(
            labelhash,
            node,
            parentNode
        );

        if (vulnerability != NameSafety.Safe) {
            return (node, vulnerability, vulnerableNode);
        }

        if (!allFusesBurned(parentNode, CANNOT_REPLACE_SUBDOMAIN)) {
            return (node, NameSafety.SubdomainReplacementAllowed, parentNode);
        }

        return (node, NameSafety.Safe, 0);
    }

    function _checkOwnership(
        bytes32 labelhash,
        bytes32 node,
        bytes32 parentNode
    ) internal view returns (NameSafety vulnerability, bytes32 vulnerableNode) {
        if (parentNode == ETH_NODE) {
            // Special case .eth: Check registrant or name isexpired

            try registrar.ownerOf(uint256(labelhash)) returns (
                address registrarOwner
            ) {
                if (registrarOwner != address(this)) {
                    return (NameSafety.RegistrantNotWrapped, node);
                }
            } catch {
                return (NameSafety.Expired, node);
            }
        }

        if (ens.owner(node) != address(this)) {
            return (NameSafety.ControllerNotWrapped, node);
        }
        return (NameSafety.Safe, 0);
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./ERC1155Fuse.sol";
import "./Controllable.sol";
import "./INameWrapper.sol";
import "./INameWrapperUpgrade.sol";
import "./IMetadataService.sol";
import "../registry/ENS.sol";
import "../ethregistrar/IBaseRegistrar.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./BytesUtil.sol";

error Unauthorised(bytes32 node, address addr);
error NameNotFound();
error IncompatibleParent();
error IncompatibleName(bytes name);
error IncorrectTokenType();
error LabelMismatch(bytes32 labelHash, bytes32 expectedLabelhash);
error LabelTooShort();
error LabelTooLong(string label);
error IncorrectTargetOwner(address owner);
error ZeroAddress();

contract NameWrapper is
    Ownable,
    ERC1155Fuse,
    INameWrapper,
    Controllable,
    IERC721Receiver
{
    using BytesUtils for bytes;
    ENS public immutable override ens;
    IBaseRegistrar public immutable override registrar;
    IMetadataService public override metadataService;
    mapping(bytes32 => bytes) public override names;

    bytes32 private constant ETH_NODE =
        0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;
    bytes32 private constant ROOT_NODE =
        0x0000000000000000000000000000000000000000000000000000000000000000;

    //A contract address to a new upgraded contract if any
    INameWrapperUpgrade public upgradeContract;

    constructor(
        ENS _ens,
        IBaseRegistrar _registrar,
        IMetadataService _metadataService
    ) {
        ens = _ens;
        registrar = _registrar;
        metadataService = _metadataService;

        /* Burn CANNOT_REPLACE_SUBDOMAIN and CANNOT_UNWRAP fuses for ROOT_NODE and ETH_NODE */

        _setData(
            uint256(ETH_NODE),
            address(0x0),
            uint96(CANNOT_REPLACE_SUBDOMAIN | CANNOT_UNWRAP)
        );
        _setData(
            uint256(ROOT_NODE),
            address(0x0),
            uint96(CANNOT_REPLACE_SUBDOMAIN | CANNOT_UNWRAP)
        );
        names[ROOT_NODE] = "\x00";
        names[ETH_NODE] = "\x03eth\x00";
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC1155Fuse, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(INameWrapper).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /* Metadata service */

    /**
     * @notice Set the metadata service. only admin can do this
     */

    function setMetadataService(IMetadataService _newMetadataService)
        public
        onlyOwner
    {
        metadataService = _newMetadataService;
    }

    /**
     * @notice Get the metadata uri
     * @return String uri of the metadata service
     */

    function uri(uint256 tokenId) public view override returns (string memory) {
        return metadataService.uri(tokenId);
    }

    /**
     * @notice Set the address of the upgradeContract of the contract. only admin can do this
     * @dev The default value of upgradeContract is the 0 address. Use the 0 address at any time
     * to make the contract not upgradable.
     * @param _upgradeAddress address of an upgraded contract
     */

    function setUpgradeContract(INameWrapperUpgrade _upgradeAddress)
        public
        onlyOwner
    {
            if (address(upgradeContract) != address(0)){
                registrar.setApprovalForAll(address(upgradeContract), false);
                ens.setApprovalForAll(address(upgradeContract), false);
            }

            upgradeContract = _upgradeAddress;

            if (address(upgradeContract) != address(0)){
                registrar.setApprovalForAll(address(upgradeContract), true);
                ens.setApprovalForAll(address(upgradeContract), true);
            }
    }

    /**
     * @notice Checks if msg.sender is the owner or approved by the owner of a name
     * @param node namehash of the name to check
     */

    modifier onlyTokenOwner(bytes32 node) {
        if (!isTokenOwnerOrApproved(node, msg.sender)) {
            revert Unauthorised(node, msg.sender);
        }

        _;
    }

    /**
     * @notice Checks if owner or approved by owner
     * @param node namehash of the name to check
     * @param addr which address to check permissions for
     * @return whether or not is owner or approved
     */

    function isTokenOwnerOrApproved(bytes32 node, address addr)
        public
        view
        override
        returns (bool)
    {
        address owner = ownerOf(uint256(node));
        return owner == addr || isApprovedForAll(owner, addr);
    }

    /**
     * @notice Gets fuse permissions for a specific name
     * @dev Fuses are represented by a uint96 where each permission is represented by 1 bit
     *      The interface has predefined fuses for all registry permissions, but additional
     *      fuses can be added for other use cases
     * @param node namehash of the name to check
     * @return fuses A number that represents the permissions a name has
     * @return vulnerability The type of vulnerability
     * @return vulnerableNode Which node is vulnerable
     */
    function getFuses(bytes32 node)
        public
        view
        override
        returns (
            uint96 fuses,
            NameSafety vulnerability,
            bytes32 vulnerableNode
        )
    {
        bytes memory name = names[node];
        if (name.length < 1) {
            revert NameNotFound();
        }
        (, vulnerability, vulnerableNode) = _checkHierarchy(name, 0);
        (, fuses) = getData(uint256(node));
    }

    /**
     * @notice Wraps a .eth domain, creating a new token and sending the original ERC721 token to this *         contract
     * @dev Can be called by the owner of the name in the .eth registrar or an authorised caller on the *      registrar
     * @param label label as a string of the .eth domain to wrap
     * @param _fuses initial fuses to set
     * @param wrappedOwner Owner of the name in this contract
     */

    function wrapETH2LD(
        string calldata label,
        address wrappedOwner,
        uint96 _fuses,
        address resolver
    ) public override {
        uint256 tokenId = uint256(keccak256(bytes(label)));
        address registrant = registrar.ownerOf(tokenId);
        if (
            registrant != msg.sender &&
            !isApprovedForAll(registrant, msg.sender) &&
            !registrar.isApprovedForAll(registrant, msg.sender)
        ) {
            revert Unauthorised(
                _makeNode(ETH_NODE, bytes32(tokenId)),
                msg.sender
            );
        }

        // transfer the token from the user to this contract
        registrar.transferFrom(registrant, address(this), tokenId);

        // transfer the ens record back to the new owner (this contract)
        registrar.reclaim(tokenId, address(this));

        _wrapETH2LD(label, wrappedOwner, _fuses, resolver);
    }

    /**
     * @dev Registers a new .eth second-level domain and wraps it.
     *      Only callable by authorised controllers.
     * @param label The label to register (Eg, 'foo' for 'foo.eth').
     * @param wrappedOwner The owner of the wrapped name.
     * @param duration The duration, in seconds, to register the name for.
     * @param resolver The resolver address to set on the ENS registry (optional).
     * @return expires The expiry date of the new name, in seconds since the Unix epoch.
     */
    function registerAndWrapETH2LD(
        string calldata label,
        address wrappedOwner,
        uint256 duration,
        address resolver,
        uint96 _fuses
    ) external override onlyController returns (uint256 expires) {
        uint256 tokenId = uint256(keccak256(bytes(label)));

        expires = registrar.register(tokenId, address(this), duration);
        _wrapETH2LD(label, wrappedOwner, _fuses, resolver);
    }

    /**
     * @dev Renews a .eth second-level domain.
     *      Only callable by authorised controllers.
     * @param tokenId The hash of the label to register (eg, `keccak256('foo')`, for 'foo.eth').
     * @param duration The number of seconds to renew the name for.
     * @return expires The expiry date of the name, in seconds since the Unix epoch.
     */
    function renew(uint256 tokenId, uint256 duration)
        external
        override
        onlyController
        returns (uint256 expires)
    {
        return registrar.renew(tokenId, duration);
    }

    /**
     * @notice Wraps a non .eth domain, of any kind. Could be a DNSSEC name vitalik.xyz or a subdomain
     * @dev Can be called by the owner in the registry or an authorised caller in the registry
     * @param name The name to wrap, in DNS format
     * @param _fuses initial fuses to set represented as a number. Check getFuses() for more info
     * @param wrappedOwner Owner of the name in this contract
     */

    function wrap(
        bytes calldata name,
        address wrappedOwner,
        uint96 _fuses,
        address resolver
    ) public override {
        (bytes32 labelhash, uint256 offset) = name.readLabel(0);
        bytes32 parentNode = name.namehash(offset);
        bytes32 node = _makeNode(parentNode, labelhash);

        if (parentNode == ETH_NODE) {
            revert IncompatibleParent();
        }

        address owner = ens.owner(node);

        if (
            owner != msg.sender &&
            !isApprovedForAll(owner, msg.sender) &&
            !ens.isApprovedForAll(owner, msg.sender)
        ) {
            revert Unauthorised(node, msg.sender);
        }

        if (resolver != address(0)) {
            ens.setResolver(node, resolver);
        }

        ens.setOwner(node, address(this));

        _wrap(node, name, wrappedOwner, _fuses);
    }

    /**
     * @notice Unwraps a .eth domain. e.g. vitalik.eth
     * @dev Can be called by the owner in the wrapper or an authorised caller in the wrapper
     * @param labelhash labelhash of the .eth domain
     * @param newRegistrant sets the owner in the .eth registrar to this address
     * @param newController sets the owner in the registry to this address
     */

    function unwrapETH2LD(
        bytes32 labelhash,
        address newRegistrant,
        address newController
    ) public override onlyTokenOwner(_makeNode(ETH_NODE, labelhash)) {
        _unwrap(_makeNode(ETH_NODE, labelhash), newController);
        registrar.transferFrom(
            address(this),
            newRegistrant,
            uint256(labelhash)
        );
    }

    /**
     * @notice Unwraps a non .eth domain, of any kind. Could be a DNSSEC name vitalik.xyz or a subdomain
     * @dev Can be called by the owner in the wrapper or an authorised caller in the wrapper
     * @param parentNode parent namehash of the name to wrap e.g. vitalik.xyz would be namehash('xyz')
     * @param labelhash labelhash of the .eth domain
     * @param newController sets the owner in the registry to this address
     */

    function unwrap(
        bytes32 parentNode,
        bytes32 labelhash,
        address newController
    ) public override onlyTokenOwner(_makeNode(parentNode, labelhash)) {
        if (parentNode == ETH_NODE) {
            revert IncompatibleParent();
        }
        _unwrap(_makeNode(parentNode, labelhash), newController);
    }

    /**
     * @notice Burns any fuse passed to this function for a name
     * @dev Fuse burns are always additive and will not unburn already burnt fuses
     * @param node namehash of the name. e.g. vitalik.xyz would be namehash('vitalik.xyz')
     * @param _fuses Fuses you want to burn.
     */

    function burnFuses(bytes32 node, uint96 _fuses)
        public
        override
        onlyTokenOwner(node)
        operationAllowed(node, CANNOT_BURN_FUSES)
    {
        (address owner, uint96 fuses) = getData(uint256(node));

        uint96 newFuses = fuses | _fuses;

        _setData(uint256(node), owner, newFuses);

        emit FusesBurned(node, newFuses);
    }

    /**
     * @notice Upgrades a .eth wrapped domain by calling the wrapETH2LD function of the upgradeAddress
     *  contract and burning the token of this contract.
     * @dev Can be called by the owner of the name in this contract
     * @param label label as a string of the .eth domain to upgrade
     * @param wrappedOwner The owner of the wrapped name.
     */

    function upgradeETH2LD(
        string calldata label,
        address wrappedOwner
    )
        public
    {

        if (address(upgradeContract) == address(0)){
            revert ZeroAddress();
        }

        bytes32 labelhash = keccak256(bytes(label));
        bytes32 node = _makeNode(ETH_NODE, labelhash);

        if (!isTokenOwnerOrApproved(node, msg.sender)){
            revert Unauthorised(node, msg.sender);
        }

        (uint96 fuses,,) = getFuses(node);

        address resolver = ens.resolver(node);

        upgradeContract.wrapETH2LD(label, wrappedOwner, fuses, resolver);

        // burn token and fuse data
        _burn(uint256(node));

    }

    /**
     * @notice Upgrades a non .eth domain of any kind. Could be a DNSSEC name vitalik.xyz or a subdomain
     * @dev Can be called by the owner or an authorised caller
     * @param name The name to upgrade, in DNS format
     * @param wrappedOwner owner of the name in this contract
     */

    function upgrade(
        bytes calldata name,
        address wrappedOwner
    ) public {

        if (address(upgradeContract) == address(0)){
            revert ZeroAddress();
        }

        (bytes32 labelhash, uint offset) = name.readLabel(0);
        bytes32 parentNode = name.namehash(offset);
        bytes32 node = _makeNode(parentNode, labelhash);

        if (!isTokenOwnerOrApproved(node, msg.sender)){
            revert Unauthorised(node, msg.sender);
        }

        address resolver = ens.resolver(node);

        (uint96 fuses,,) = getFuses(node);

        upgradeContract.wrap(name, wrappedOwner, fuses, resolver);

        // burn token and fuse data
        _burn(uint256(node));
    }

    /**
     * @notice Sets records for the subdomain in the ENS Registry
     * @param parentNode namehash of the parent name
     * @param labelhash labelhash of the subnode
     * @param owner newOwner in the registry
     * @param resolver the resolver contract in the registry
     * @param ttl ttl in the registry
     */

    function setSubnodeRecord(
        bytes32 parentNode,
        bytes32 labelhash,
        address owner,
        address resolver,
        uint64 ttl
    )
        public
        override
        onlyTokenOwner(parentNode)
        canCallSetSubnodeOwner(parentNode, labelhash)
    {
        ens.setSubnodeRecord(parentNode, labelhash, owner, resolver, ttl);
    }

    /**
     * @notice Sets the subnode owner in the registry
     * @param parentNode namehash of the parent name
     * @param labelhash labelhash of the subnode
     * @param owner newOwner in the registry
     */

    function setSubnodeOwner(
        bytes32 parentNode,
        bytes32 labelhash,
        address owner
    )
        public
        override
        onlyTokenOwner(parentNode)
        canCallSetSubnodeOwner(parentNode, labelhash)
        returns (bytes32)
    {
        return ens.setSubnodeOwner(parentNode, labelhash, owner);
    }

    /**
     * @notice Sets the subdomain owner in the registry and then wraps the subdomain
     * @param parentNode parent namehash of the subdomain
     * @param label label of the subdomain as a string
     * @param newOwner newOwner in the registry
     * @param _fuses initial fuses for the wrapped subdomain
     */

    function setSubnodeOwnerAndWrap(
        bytes32 parentNode,
        string calldata label,
        address newOwner,
        uint96 _fuses
    ) public override returns (bytes32 node) {
        bytes32 labelhash = keccak256(bytes(label));
        node = _makeNode(parentNode, labelhash);
        bytes memory name = _addLabel(label, names[parentNode]);

        setSubnodeOwner(parentNode, labelhash, address(this));

        _wrap(node, name, newOwner, _fuses);
    }

    /**
     * @notice Sets the subdomain owner in the registry with records and then wraps the subdomain
     * @param parentNode parent namehash of the subdomain
     * @param label label of the subdomain as a string
     * @param newOwner newOwner in the registry
     * @param resolver resolver contract in the registry
     * @param ttl ttl in the regsitry
     * @param _fuses initial fuses for the wrapped subdomain
     */

    function setSubnodeRecordAndWrap(
        bytes32 parentNode,
        string calldata label,
        address newOwner,
        address resolver,
        uint64 ttl,
        uint96 _fuses
    ) public override {
        bytes32 labelhash = keccak256(bytes(label));
        bytes32 node = _makeNode(parentNode, labelhash);
        bytes memory name = _addLabel(label, names[parentNode]);

        setSubnodeRecord(parentNode, labelhash, address(this), resolver, ttl);

        _wrap(node, name, newOwner, _fuses);
    }

    /**
     * @notice Sets records for the name in the ENS Registry
     * @param node namehash of the name to set a record for
     * @param owner newOwner in the registry
     * @param resolver the resolver contract
     * @param ttl ttl in the registry
     */

    function setRecord(
        bytes32 node,
        address owner,
        address resolver,
        uint64 ttl
    )
        public
        override
        onlyTokenOwner(node)
        operationAllowed(
            node,
            CANNOT_TRANSFER | CANNOT_SET_RESOLVER | CANNOT_SET_TTL
        )
    {
        ens.setRecord(node, owner, resolver, ttl);
    }

    /**
     * @notice Sets resolver contract in the registry
     * @param node namehash of the name
     * @param resolver the resolver contract
     */

    function setResolver(bytes32 node, address resolver)
        public
        override
        onlyTokenOwner(node)
        operationAllowed(node, CANNOT_SET_RESOLVER)
    {
        ens.setResolver(node, resolver);
    }

    /**
     * @notice Sets TTL in the registry
     * @param node namehash of the name
     * @param ttl TTL in the registry
     */

    function setTTL(bytes32 node, uint64 ttl)
        public
        override
        onlyTokenOwner(node)
        operationAllowed(node, CANNOT_SET_TTL)
    {
        ens.setTTL(node, ttl);
    }

    /**
     * @dev Allows an operation only if none of the specified fuses are burned.
     * @param node The namehash of the name to check fuses on.
     * @param fuseMask A bitmask of fuses that must not be burned.
     */
    modifier operationAllowed(bytes32 node, uint96 fuseMask) {
        (, uint96 fuses) = getData(uint256(node));
        if (fuses & fuseMask != 0) {
            revert OperationProhibited(node);
        }
        _;
    }

    /**
     * @notice Check whether a name can call setSubnodeOwner/setSubnodeRecord
     * @dev Checks both canCreateSubdomain and canReplaceSubdomain and whether not they have been burnt
     *      and checks whether the owner of the subdomain is 0x0 for creating or already exists for
     *      replacing a subdomain. If either conditions are true, then it is possible to call
     *      setSubnodeOwner
     * @param node namehash of the name to check
     * @param labelhash labelhash of the name to check
     */

    modifier canCallSetSubnodeOwner(bytes32 node, bytes32 labelhash) {
        bytes32 subnode = _makeNode(node, labelhash);
        address owner = ens.owner(subnode);
        (, uint96 fuses) = getData(uint256(node));

        if (
            (owner == address(0) && fuses & CANNOT_CREATE_SUBDOMAIN != 0) ||
            (owner != address(0) && fuses & CANNOT_REPLACE_SUBDOMAIN != 0)
        ) {
            revert OperationProhibited(node);
        }

        _;
    }

    /**
     * @notice Checks all Fuses in the mask are burned for the node
     * @param node namehash of the name
     * @param fuseMask the fuses you want to check
     * @return Boolean of whether or not all the selected fuses are burned
     */

    function allFusesBurned(bytes32 node, uint96 fuseMask)
        public
        view
        override
        returns (bool)
    {
        (, uint96 fuses) = getData(uint256(node));
        return fuses & fuseMask == fuseMask;
    }

    function onERC721Received(
        address to,
        address,
        uint256 tokenId,
        bytes calldata data
    ) public override returns (bytes4) {
        //check if it's the eth registrar ERC721
        if (msg.sender != address(registrar)) {
            revert IncorrectTokenType();
        }

        (
            string memory label,
            address owner,
            uint96 fuses,
            address resolver
        ) = abi.decode(data, (string, address, uint96, address));

        bytes32 labelhash = bytes32(tokenId);
        bytes32 labelhashFromData = keccak256(bytes(label));

        if (labelhashFromData != labelhash) {
            revert LabelMismatch(labelhashFromData, labelhash);
        }

        // transfer the ens record back to the new owner (this contract)
        registrar.reclaim(uint256(labelhash), address(this));

        _wrapETH2LD(label, owner, fuses, resolver);

        return IERC721Receiver(to).onERC721Received.selector;
    }

    /***** Internal functions */

    function _canTransfer(uint96 fuses) internal pure override returns (bool) {
        return fuses & CANNOT_TRANSFER == 0;
    }

    function _makeNode(bytes32 node, bytes32 labelhash)
        private
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(node, labelhash));
    }

    function _addLabel(string memory label, bytes memory name)
        internal
        pure
        returns (bytes memory ret)
    {
        if (bytes(label).length < 1) {
            revert LabelTooShort();
        }
        if (bytes(label).length > 255) {
            revert LabelTooLong(label);
        }
        return abi.encodePacked(uint8(bytes(label).length), label, name);
    }

    function _mint(
        bytes32 node,
        address wrappedOwner,
        uint96 _fuses
    ) internal override {
        address oldWrappedOwner = ownerOf(uint256(node));
        if (oldWrappedOwner != address(0)) {
            // burn and unwrap old token of old owner
            _burn(uint256(node));
            emit NameUnwrapped(node, address(0));
        }
        super._mint(node, wrappedOwner, _fuses);
    }

    function _wrap(
        bytes32 node,
        bytes memory name,
        address wrappedOwner,
        uint96 fuses
    ) internal {
        names[node] = name;

        _mint(node, wrappedOwner, fuses);

        emit NameWrapped(node, name, wrappedOwner, fuses);
    }

    function _wrapETH2LD(
        string memory label,
        address wrappedOwner,
        uint96 _fuses,
        address resolver
    ) private returns (bytes32 labelhash) {
        labelhash = keccak256(bytes(label));
        bytes32 node = _makeNode(ETH_NODE, labelhash);
        bytes memory name = _addLabel(label, "\x03eth\x00");

        if (resolver != address(0)) {
            ens.setResolver(node, resolver);
        }

        // mint a new ERC1155 token with fuses
        _wrap(node, name, wrappedOwner, _fuses);
    }

    function _unwrap(bytes32 node, address newOwner) private {
        if (newOwner == address(0x0) || newOwner == address(this)) {
            revert IncorrectTargetOwner(newOwner);
        }

        if (allFusesBurned(node, CANNOT_UNWRAP)) {
            revert OperationProhibited(node);
        }

        // burn token and fuse data
        _burn(uint256(node));
        ens.setOwner(node, newOwner);

        emit NameUnwrapped(node, newOwner);
    }

    function _setData(
        uint256 tokenId,
        address owner,
        uint96 fuses
    ) internal override {
        if (fuses != CAN_DO_EVERYTHING && fuses & CANNOT_UNWRAP == 0) {
            revert OperationProhibited(bytes32(tokenId));
        }
        super._setData(tokenId, owner, fuses);
    }

    /**
     * @dev Internal function that checks all a name's ancestors to ensure fuse values will be respected and parent controller/registrant are set to the Wrapper
     * @param name The name to check.
     * @param offset The offset into the name to start at.
     * @return node The calculated namehash for this part of the name.
     * @return vulnerability what kind of vulnerability the node has
     * @return vulnerableNode which node is at risk
     */
    function _checkHierarchy(bytes memory name, uint256 offset)
        internal
        view
        returns (
            bytes32 node,
            NameSafety vulnerability,
            bytes32 vulnerableNode
        )
    {
        // Read the first label. If it's the root, return immediately.
        (bytes32 labelhash, uint256 newOffset) = name.readLabel(offset);
        if (labelhash == bytes32(0)) {
            // Root node
            return (bytes32(0), NameSafety.Safe, 0);
        }

        // Check the parent name
        bytes32 parentNode;
        (parentNode, vulnerability, vulnerableNode) = _checkHierarchy(
            name,
            newOffset
        );

        node = _makeNode(parentNode, labelhash);

        // stop function checking any other nodes if a parent is not safe
        if (vulnerability != NameSafety.Safe) {
            return (node, vulnerability, vulnerableNode);
        }

        // Check the parent name's fuses to see if replacing subdomains is forbidden
        if (parentNode == ROOT_NODE) {
            // Save ourselves some gas; root node can't be replaced
            return (node, NameSafety.Safe, 0);
        }

        (vulnerability, vulnerableNode) = _checkOwnership(
            labelhash,
            node,
            parentNode
        );

        if (vulnerability != NameSafety.Safe) {
            return (node, vulnerability, vulnerableNode);
        }

        if (!allFusesBurned(parentNode, CANNOT_REPLACE_SUBDOMAIN)) {
            return (node, NameSafety.SubdomainReplacementAllowed, parentNode);
        }

        return (node, NameSafety.Safe, 0);
    }

    function _checkOwnership(
        bytes32 labelhash,
        bytes32 node,
        bytes32 parentNode
    ) internal view returns (NameSafety vulnerability, bytes32 vulnerableNode) {
        if (parentNode == ETH_NODE) {
            // Special case .eth: Check registrant or name isexpired

            try registrar.ownerOf(uint256(labelhash)) returns (
                address registrarOwner
            ) {
                if (registrarOwner != address(this)) {
                    return (NameSafety.RegistrantNotWrapped, node);
                }
            } catch {
                return (NameSafety.Expired, node);
            }
        }

        if (ens.owner(node) != address(this)) {
            return (NameSafety.ControllerNotWrapped, node);
        }
        return (NameSafety.Safe, 0);
    }
}


Fix indentation

Fix indentation

import './Conference.sol';
import './zeppelin/PullPaymentCapable.sol';

/*
 * Bounty
 * This bounty will pay out if you can cause a Conference's balance
 * to be different from its totalBalance, which would mean that it doesn't
 * have sufficient ether for everyone to withdraw.
 */
contract Bounty is PullPaymentCapable {
  uint public totalBounty;
  bool public claimed;
  mapping(address => address) public researchers;
  event TargetCreation(address createdAddress);
  event Error(string message);
  function contribute() {
    totalBounty = msg.value;
    if (claimed) throw;
  }

  function createTarget() returns(Conference) {
    Conference target = new Conference();
    researchers[target] = msg.sender;
    TargetCreation(target);
    return target;
  }

  modifier hasResearcher(Conference target) {
    address researcher = researchers[target];
    if (researcher != 0){
      _
    }else{
      Error('there are no researcher');
    }
	}

  modifier hasBug(Conference target) {
    if (target.totalBalance() != target.balance){
      _
    }else{
      Error('Security breach!!');
    }
  }

  function claim(Conference target) hasResearcher(target) hasBug(target){
    address researcher = researchers[target];
    asyncSend(researcher, this.balance);
    totalBounty = 0;
    claimed = true;
  }
}


calculate merkle root of contract arguments

pragma solidity ^0.4.19;

// generic merkle proofs for keccak256

contract Merkle {

   function bytes32ToBytes(bytes32 x) internal pure returns (bytes) {
     bytes memory res = new bytes(32);
     assembly {
       mstore(add(res,32), x)
     }
     return res;
   }

   function process(bytes32 leaf, bytes[] inst) internal pure returns (bytes32) {
      // first is the leaf
      for (uint i = 0; i+1 < inst.length; i += 2) {
         leaf = keccak256(inst[i], leaf, inst[i+1]);
      }
      return leaf;
   }

   function slice(bytes arr, uint i, uint n) internal pure returns (bytes) {
       bytes memory res = new bytes(n);
       for (uint j = 0; j < n; j++) res[j] = arr[i+j];
       return res;
   }

   function slice2(bytes arr, uint i1, uint i2) internal pure returns (bytes) {
       bytes memory res = new bytes(i2-i1);
       for (uint j = 0; j < i2-i1; j++) res[j] = arr[i1+j];
       return res;
   }

   // basically the merkle root of the argument has to be calculated
   // perhaps just require that it has to be padded?
   function process2(bytes32 leaf, uint[] ctrl, bytes inst) public pure returns (bytes32) {
      // first is the leaf
      for (uint i = 0; i+2 < ctrl.length; i += 2) {
         leaf = keccak256(slice2(inst, ctrl[i], ctrl[i+1]),
                          leaf,
                          slice2(inst, ctrl[i+1], ctrl[i+2]));
      }
      return leaf;
   }

   function getRoot(bytes32 leaf, uint[] ctrl, bytes inst, uint sz) public pure returns (bytes32) {
       return dataMerkle(0, sz);
   }

   function dataMerkle(uint idx, uint level) internal pure returns (bytes32) {
      if (level == 0) {
          if (idx*4+32 < msg.data.length) {
              // get the element
              bytes32 elem;
              uint d_idx = idx*4+32;
              assembly {
                  elem := calldataload(d_idx)
              }
              return keccak256(bytes16(elem), uint128(elem));
          }
          else return keccak256(bytes16(0), bytes16(0));
      }
      else return keccak256(dataMerkle(idx, level-1), dataMerkle(idx+(2**(level-1)), level-1));
   }

   function test(bytes dta) public pure returns (bytes32) {
       return keccak256(dta);
   }

   function test2(bytes dta, uint n) public pure {
       slice2(dta, 0, n);
   }

   function test3() public pure returns (bytes32) {
       return keccak256(bytes2(0x0));
   }

}

pragma solidity ^0.4.19;

// generic merkle proofs for keccak256

contract Merkle {

   function bytes32ToBytes(bytes32 x) internal pure returns (bytes) {
     bytes memory res = new bytes(32);
     assembly {
       mstore(add(res,32), x)
     }
     return res;
   }

   function process(bytes32 leaf, bytes[] inst) internal pure returns (bytes32) {
      // first is the leaf
      for (uint i = 0; i+1 < inst.length; i += 2) {
         leaf = keccak256(inst[i], leaf, inst[i+1]);
      }
      return leaf;
   }

   function slice(bytes arr, uint i, uint n) internal pure returns (bytes) {
       bytes memory res = new bytes(n);
       for (uint j = 0; j < n; j++) res[j] = arr[i+j];
       return res;
   }

   function slice2(bytes arr, uint i1, uint i2) internal pure returns (bytes) {
       bytes memory res = new bytes(i2-i1);
       for (uint j = 0; j < i2-i1; j++) res[j] = arr[i1+j];
       return res;
   }

   function process2(bytes32 leaf, bytes inst, uint[] ctrl) public pure returns (bytes32) {
      // first is the leaf
      for (uint i = 0; i+2 < ctrl.length; i += 2) {
         leaf = keccak256(slice2(inst, ctrl[i], ctrl[i+1]),
                          leaf,
                          slice2(inst, ctrl[i+1], ctrl[i+2]));
      }
      return leaf;
   }

   function test(bytes dta) public pure returns (bytes32) {
       return keccak256(dta);
   }

   function test2(bytes dta, uint n) public pure {
       slice2(dta, 0, n);
   }

}


Reorder state changes and event emission for consistency (#2719)

Co-authored-by: Francisco Giordano <4223852eec80914c4c190cdc031e06592b151a67@gmail.com>

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}


Fix for solidity compiler warnings, issue #367. Crazy and temporary solution, but it's working.

pragma solidity ^0.4.11;

import "./CanReclaimToken.sol";

/**
 * @title Contracts that should not own Tokens
 * @author Remco Bloemen <remco@2π.com>
 * @dev This blocks incoming ERC23 tokens to prevent accidental loss of tokens.
 * Should tokens (any ERC20Basic compatible) end up in the contract, it allows the
 * owner to reclaim the tokens.
 */
contract HasNoTokens is CanReclaimToken {

 /**
  * @dev Reject all ERC23 compatible tokens
  * @param from_ address The address that is transferring the tokens
  * @param value_ uint256 the amount of the specified token
  * @param data_ Bytes The data passed from the caller.
  */
  function tokenFallback(address from_, uint256 value_, bytes data_) external {
    // revert();
    if(true || from_ == 0 || value_ == 0 || data_.length == 0 ) revert(); // Temporary fix for solidity compiler warning
  }

}

pragma solidity ^0.4.11;

import "./CanReclaimToken.sol";

/**
 * @title Contracts that should not own Tokens
 * @author Remco Bloemen <remco@2π.com>
 * @dev This blocks incoming ERC23 tokens to prevent accidental loss of tokens.
 * Should tokens (any ERC20Basic compatible) end up in the contract, it allows the
 * owner to reclaim the tokens.
 */
contract HasNoTokens is CanReclaimToken {

 /**
  * @dev Reject all ERC23 compatible tokens
  * @param from_ address The address that is transferring the tokens
  * @param value_ uint256 the amount of the specified token
  * @param data_ Bytes The data passed from the caller.
  */
  function tokenFallback(address from_, uint256 value_, bytes data_) external {
    revert();
  }

}


first pass

first pass

import "GovContracts.sol";

contract Payouts is GovContracts {

function calculatePayout(uint contractId, uint milestoneId, Constraint constraint) returns (uint payout){
    payee = msg.sender;
    vote = contracts[contractId].milestones[milestoneId].Votes[constraint];
    payout = 0;

    if(vote.resolved == true){
        uint winningVotes;
        uint losingVotes;
        uint originalContribution;

        if(vote.globalVote.positive > vote.globalVote.negative){
            //positives won
            winningVotes = vote.globalVote.positive;
            losingVotes = vote.globalVote.negative;
            originalContribution = contracts[contractId].milestones[milestoneId].Votes[constraint].vote[payee].positive;
        }
        else{
            //negatives won
            winningVotes = vote.globalVote.negative;
            losingVotes = vote.globalVote.positive;
            originalContribution = contracts[contractId].milestones[milestoneId].Votes[constraint].vote[payee].negative;
        }
        //calculate payout
        uint individualPayout = 0;
        individualPayout = originalContribution * (1+ losingVotes/winningVotes);
    }
    return payout;
}

function payout (uint contractId, uint milestoneId, Constraint constraint) private {
    payee = msg.sender;
    uint payout = calculatePayout(contractId, milestoneId, constraint);
    if(payout > 0){
        //zero out account so that the sender cannot payout more than once
        contracts[contractId].milestones[milestoneId].Votes[constraint].vote[payee].negative = 0;
        contracts[contractId].milestones[milestoneId].Votes[constraint].vote[payee].positive = 0;
        if(!payee.send(payout)){
            throw; //if we can't pay out then throw will revert the account changes above
        }
    }
}

function payoutForTimeline(uint contractId, uint milestoneId){
    payout(contractId, milestoneId, Constraint.Timeline);
}
function payoutForBudget(uint contractId, uint milestoneId){
    payout(contractId, milestoneId, Constraint.Budget);
}

}

error: pathspec 'contracts/Payouts.sol' did not match any file(s) known to git


Add test that circular types don't hang the decoder

pragma solidity ^0.5.10;
pragma experimental ABIEncoderV2;

contract WireTestParent {

  event Done();

  function inherited(uint[2] memory) public {
    emit Done();
  }

  //no constructor
}

contract WireTest is WireTestParent {
  constructor(bool status, bytes memory info, Ternary whoknows) public {
    deepStruct["blornst"].length = 9;
    deepString.length = 9;
    emit ConstructorEvent(status, info, whoknows);
  }

  event ConstructorEvent(bool bit, bytes, Ternary);

  struct Triple {
    int x;
    bytes32 y;
    bytes z;
  }

  //the point of including this is to test that it doesn't send
  //the allocator into an infinite loop
  struct RedHerring {
    RedHerring[] redHerring;
  }

  //similarly
  function redHerring(RedHerring memory) internal pure {
  }

  enum Ternary {
    Yes, No, MaybeSo
  }

  event EmitStuff(Triple, address[2], string[]);

  function emitStuff(Triple memory p, address[2] memory precompiles, string[] memory strings) public {
    emit EmitStuff(p, precompiles, strings);
  }

  event MoreStuff(WireTest, uint[] data);

  function moreStuff(WireTest notThis, uint[] memory bunchOfInts) public {
    emit MoreStuff(notThis, bunchOfInts);
  }

  event Danger(function() external);

  function danger() public {
    emit Danger(this.danger);
  }

  event HasIndices(uint, uint indexed, string, string indexed, uint);

  function indexTest(uint a, uint b, string memory c, string memory d, uint e) public {
    emit HasIndices(a, b, c, d, e);
  }

  function libraryTest(string memory it) public {
    WireTestLibrary.emitEvent(it);
  }

  event AmbiguousEvent(uint8[] indexed, uint[5]);

  function ambiguityTest() public {
    uint8[] memory short = new uint8[](3);
    uint[5] memory long;
    long[0] = 32;
    long[1] = 3;
    long[2] = short[0] = 17;
    long[3] = short[1] = 18;
    long[4] = short[2] = 19;
    emit AmbiguousEvent(short, long);
  }

  function unambiguityTest() public {
    uint8[] memory empty;
    uint[5] memory tooLong;
    //array length too long
    tooLong[0] = 32;
    tooLong[1] = 1e12; //still small enough for JS :)
    tooLong[2] = 17;
    tooLong[3] = 18;
    tooLong[4] = 19;
    emit AmbiguousEvent(empty, tooLong);

    //bad padding
    uint[5] memory badPadding;
    badPadding[0] = 32;
    badPadding[1] = 3;
    badPadding[2] = 257;
    badPadding[3] = 257;
    badPadding[4] = 257;
    emit AmbiguousEvent(empty, badPadding);

    //decodes, but fails re-encode
    uint[5] memory nonStrict;
    nonStrict[0] = 64;
    nonStrict[1] = 0;
    nonStrict[2] = 2;
    nonStrict[3] = 1;
    nonStrict[4] = 1;
    emit AmbiguousEvent(empty, nonStrict);

    WireTestLibrary.emitUnambiguousEvent();
  }

  event AnonUints(uint indexed, uint indexed, uint indexed, uint indexed) anonymous;
  event NonAnon(uint indexed, uint indexed, uint indexed);
  event ObviouslyAnon(byte) anonymous;

  function anonymousTest() public {
    //first test: unambiguous
    emit AnonUints(257, 1, 1, 1);
    //second test: uint8 (from library) or uint?
    emit AnonUints(1, 2, 3, 4);
    //third test: uint, or not anonymous?
    emit NonAnon(1, 2, 3);
    //fourth test: no selector
    emit ObviouslyAnon(0xfe);
  }

  mapping(string => Triple[]) public deepStruct;
  mapping(string => string)[] public deepString;
}

library WireTestLibrary {
  event LibraryEvent(string);

  function emitEvent(string calldata it) external {
    emit LibraryEvent(it);
  }

  event AmbiguousEvent(uint8[], uint[5] indexed);

  function emitUnambiguousEvent() external {
    uint8[] memory wrongLength = new uint8[](1);
    wrongLength[0] = 107;
    uint[5] memory allZeroes;
    emit AmbiguousEvent(wrongLength, allZeroes);
  }

  event AnonUint8s(uint8 indexed, uint8 indexed, uint8 indexed, uint8 indexed) anonymous;
}

pragma solidity ^0.5.10;
pragma experimental ABIEncoderV2;

contract WireTestParent {

  event Done();

  function inherited(uint[2] memory) public {
    emit Done();
  }

  //no constructor
}

contract WireTest is WireTestParent {
  constructor(bool status, bytes memory info, Ternary whoknows) public {
    deepStruct["blornst"].length = 9;
    deepString.length = 9;
    emit ConstructorEvent(status, info, whoknows);
  }

  event ConstructorEvent(bool bit, bytes, Ternary);

  struct Triple {
    int x;
    bytes32 y;
    bytes z;
  }

  enum Ternary {
    Yes, No, MaybeSo
  }

  event EmitStuff(Triple, address[2], string[]);

  function emitStuff(Triple memory p, address[2] memory precompiles, string[] memory strings) public {
    emit EmitStuff(p, precompiles, strings);
  }

  event MoreStuff(WireTest, uint[] data);

  function moreStuff(WireTest notThis, uint[] memory bunchOfInts) public {
    emit MoreStuff(notThis, bunchOfInts);
  }

  event Danger(function() external);

  function danger() public {
    emit Danger(this.danger);
  }

  event HasIndices(uint, uint indexed, string, string indexed, uint);

  function indexTest(uint a, uint b, string memory c, string memory d, uint e) public {
    emit HasIndices(a, b, c, d, e);
  }

  function libraryTest(string memory it) public {
    WireTestLibrary.emitEvent(it);
  }

  event AmbiguousEvent(uint8[] indexed, uint[5]);

  function ambiguityTest() public {
    uint8[] memory short = new uint8[](3);
    uint[5] memory long;
    long[0] = 32;
    long[1] = 3;
    long[2] = short[0] = 17;
    long[3] = short[1] = 18;
    long[4] = short[2] = 19;
    emit AmbiguousEvent(short, long);
  }

  function unambiguityTest() public {
    uint8[] memory empty;
    uint[5] memory tooLong;
    //array length too long
    tooLong[0] = 32;
    tooLong[1] = 1e12; //still small enough for JS :)
    tooLong[2] = 17;
    tooLong[3] = 18;
    tooLong[4] = 19;
    emit AmbiguousEvent(empty, tooLong);

    //bad padding
    uint[5] memory badPadding;
    badPadding[0] = 32;
    badPadding[1] = 3;
    badPadding[2] = 257;
    badPadding[3] = 257;
    badPadding[4] = 257;
    emit AmbiguousEvent(empty, badPadding);

    //decodes, but fails re-encode
    uint[5] memory nonStrict;
    nonStrict[0] = 64;
    nonStrict[1] = 0;
    nonStrict[2] = 2;
    nonStrict[3] = 1;
    nonStrict[4] = 1;
    emit AmbiguousEvent(empty, nonStrict);

    WireTestLibrary.emitUnambiguousEvent();
  }

  event AnonUints(uint indexed, uint indexed, uint indexed, uint indexed) anonymous;
  event NonAnon(uint indexed, uint indexed, uint indexed);
  event ObviouslyAnon(byte) anonymous;

  function anonymousTest() public {
    //first test: unambiguous
    emit AnonUints(257, 1, 1, 1);
    //second test: uint8 (from library) or uint?
    emit AnonUints(1, 2, 3, 4);
    //third test: uint, or not anonymous?
    emit NonAnon(1, 2, 3);
    //fourth test: no selector
    emit ObviouslyAnon(0xfe);
  }

  mapping(string => Triple[]) public deepStruct;
  mapping(string => string)[] public deepString;
}

library WireTestLibrary {
  event LibraryEvent(string);

  function emitEvent(string calldata it) external {
    emit LibraryEvent(it);
  }

  event AmbiguousEvent(uint8[], uint[5] indexed);

  function emitUnambiguousEvent() external {
    uint8[] memory wrongLength = new uint8[](1);
    wrongLength[0] = 107;
    uint[5] memory allZeroes;
    emit AmbiguousEvent(wrongLength, allZeroes);
  }

  event AnonUint8s(uint8 indexed, uint8 indexed, uint8 indexed, uint8 indexed) anonymous;
}


all works - no "dynamic" presaale min cont

pragma solidity ^0.4.11;
import './SmartTokenController.sol';
import './Utils.sol';
import './interfaces/ISmartToken.sol';

/*
    Crowdsale v0.1

    The crowdsale version of the smart token controller, allows contributing ether in exchange for Rootcoin tokens
    The price remains fixed for the entire duration of the crowdsale
    Note that 20% of the contributions are the Bancor token's reserve
    Presale contributes are allocated (manually) with additional 20% tokens from the beneficiary tokens.
*/
contract CrowdsaleController is SmartTokenController {
    uint256 public constant PRESALE_DURATION = 14 days;                 // pressale duration
    //uint256 public constant PRESALE_MIN_CONTRIBUTION = 200 wei;      // pressale min contribution
    uint256 public constant DURATION = 14 days;                 // crowdsale duration
    uint256 public constant TOKEN_PRICE_N = 1;                  // initial price in wei (numerator)
    uint256 public constant TOKEN_PRICE_D = 0.001 ether;                // initial price in wei (denominator)
    uint256 public constant MAX_GAS_PRICE = 50000000000 wei;    // maximum gas price for contribution transactions

    string public version = "0.1";

    uint256 public startTime = 0;                   // crowdsale start time (in seconds)
    uint256 public endTime = 0;                     // crowdsale end time (in seconds)
    uint256 public totalEtherCap = 1000000 ether;   // current ether contribution cap, initialized with a temp value as a safety mechanism until the real cap is revealed
    uint256 public totalEtherContributed = 0;       // ether contributed so far
    address public beneficiary = 0x0;               // address to receive all ether contributions
    uint256 public presaleMinContribution = 200;      // pressale min contribution

    // triggered on each contribution
    event Contribution(address indexed _contributor, uint256 _amount, uint256 _return);

    /**
        @dev constructor

        @param _token          smart token the crowdsale is for
        @param _startTime      crowdsale start time
        @param _beneficiary    address to receive all ether contributions
    */
    function CrowdsaleController(ISmartToken _token, uint256 _startTime, address _beneficiary)
        SmartTokenController(_token)
        validAddress(_beneficiary)
        earlierThan(_startTime)
    {
        startTime = _startTime;
        endTime = startTime + DURATION;
        beneficiary = _beneficiary;
    }

    // verifies that the gas price is lower than 50 gwei
    modifier validGasPrice() {
        assert(tx.gasprice <= MAX_GAS_PRICE);
        _;
    }

    // ensures that it's earlier than the given time
    modifier earlierThan(uint256 _time) {
        assert(now < _time);
        _;
    }

    // ensures that the current time is between _startTime (inclusive) and _endTime (exclusive)
    modifier between(uint256 _startTime, uint256 _endTime) {
        assert(now >= _startTime && now < _endTime);
        _;
    }

    // ensures that we didn't reach the ether cap
    modifier etherCapNotReached(uint256 _contribution) {
        assert(safeAdd(totalEtherContributed, _contribution) <= totalEtherCap);
        _;
    }

    // verifies that the presale contribution is more than presale minimum
    modifier validatePresaleMinPrice() {
        require(msg.value >= presaleMinContribution);
        _;
    }

    // verifies that the presale contribution is from predefined address - TBD (not in use unless we decide to make a whitelist.)
    modifier validatePresaleAddress() {
        _;
    }

    /**
        @dev computes the number of tokens that should be issued for a given contribution

        @param _contribution    contribution amount

        @return computed number of tokens
    */
    function computeReturn(uint256 _contribution) public constant returns (uint256) {
        return safeMul(_contribution, TOKEN_PRICE_D) / TOKEN_PRICE_N;
    }

    /**
        @dev ETH contribution
        can only be called during the crowdsale

        @return tokens issued in return
    */
    function contributeETH()
        public
        payable
        between(startTime, endTime)
        returns (uint256 amount)
    {
        return processContribution();
    }

     /**
        @dev Contribution during presale (min 200 ether)
        can only be called 14 days before the crowdsale start date

        @return tokens issued in return
    */
    function contributePreSale()
        public
        payable
        between(safeSub(startTime,PRESALE_DURATION), startTime)
        validatePresaleMinPrice
        validatePresaleAddress
        returns (uint256 amount)
    {
        return processContribution();
    }

    /**
        @dev handles contribution logic
        note that the Contribution event is triggered using the sender as the contributor, regardless of the actual contributor

        @return tokens issued in return
    */
    function processContribution() private
        active
        etherCapNotReached(msg.value)
        validGasPrice
        returns (uint256 amount)
    {
        uint256 tokenAmount = computeReturn(msg.value);
        assert(beneficiary.send(msg.value)); // transfer the ether to the beneficiary account
        totalEtherContributed = safeAdd(totalEtherContributed, msg.value); // update the total contribution amount
        token.issue(msg.sender, tokenAmount); // issue new funds to the contributor in the smart token
        token.issue(beneficiary, tokenAmount); // issue tokens to the beneficiary

        Contribution(msg.sender, msg.value, tokenAmount);
        return tokenAmount;
    }

    // fallback
    function() payable {
        contributeETH();
    }
}

pragma solidity ^0.4.11;
import './SmartTokenController.sol';
import './Utils.sol';
import './interfaces/ISmartToken.sol';

/*
    Crowdsale v0.1

    The crowdsale version of the smart token controller, allows contributing ether in exchange for Rootcoin tokens
    The price remains fixed for the entire duration of the crowdsale
    Note that 20% of the contributions are the Bancor token's reserve
    Presale contributes are allocated (manually) with additional 20% tokens from the beneficiary tokens.
*/
contract CrowdsaleController is SmartTokenController {
    uint256 public constant PRESALE_DURATION = 14 days;                 // pressale duration
    uint public constant PRESALE_MIN_CONTRIBUTION = 200 wei;      // pressale min contribution
    uint256 public constant DURATION = 14 days;                 // crowdsale duration
    uint256 public constant TOKEN_PRICE_N = 1;                  // initial price in wei (numerator)
    uint256 public constant TOKEN_PRICE_D = 0.001 ether;                // initial price in wei (denominator)
    uint256 public constant MAX_GAS_PRICE = 50000000000 wei;    // maximum gas price for contribution transactions

    string public version = "0.1";

    uint256 public startTime = 0;                   // crowdsale start time (in seconds)
    uint256 public endTime = 0;                     // crowdsale end time (in seconds)
    uint256 public totalEtherCap = 1000000 ether;   // current ether contribution cap, initialized with a temp value as a safety mechanism until the real cap is revealed
    uint256 public totalEtherContributed = 0;       // ether contributed so far
    address public beneficiary = 0x0;               // address to receive all ether contributions

    // triggered on each contribution
    event Contribution(address indexed _contributor, uint256 _amount, uint256 _return);

    /**
        @dev constructor

        @param _token          smart token the crowdsale is for
        @param _startTime      crowdsale start time
        @param _beneficiary    address to receive all ether contributions
    */
    function CrowdsaleController(ISmartToken _token, uint256 _startTime, address _beneficiary)
        SmartTokenController(_token)
        validAddress(_beneficiary)
        earlierThan(_startTime)
    {
        startTime = _startTime;
        endTime = startTime + DURATION;
        beneficiary = _beneficiary;
    }

    // verifies that the gas price is lower than 50 gwei
    modifier validGasPrice() {
        assert(tx.gasprice <= MAX_GAS_PRICE);
        _;
    }

    // ensures that it's earlier than the given time
    modifier earlierThan(uint256 _time) {
        assert(now < _time);
        _;
    }

    // ensures that the current time is between _startTime (inclusive) and _endTime (exclusive)
    modifier between(uint256 _startTime, uint256 _endTime) {
        assert(now >= _startTime && now < _endTime);
        _;
    }

    // ensures that we didn't reach the ether cap
    modifier etherCapNotReached(uint256 _contribution) {
        assert(safeAdd(totalEtherContributed, _contribution) <= totalEtherCap);
        _;
    }

    // verifies that the presale contribution is more than presale minimum
    modifier validatePresaleMinPrice() {
        require(msg.value >= PRESALE_MIN_CONTRIBUTION);
        _;
    }

    // verifies that the presale contribution is from predefined address - TBD (not in use unless we decide to make a whitelist.)
    modifier validatePresaleAddress() {
        _;
    }

    /**
        @dev computes the number of tokens that should be issued for a given contribution

        @param _contribution    contribution amount

        @return computed number of tokens
    */
    function computeReturn(uint256 _contribution) public constant returns (uint256) {
        return safeMul(_contribution, TOKEN_PRICE_D) / TOKEN_PRICE_N;
    }

    /**
        @dev ETH contribution
        can only be called during the crowdsale

        @return tokens issued in return
    */
    function contributeETH()
        public
        payable
        between(startTime, endTime)
        returns (uint256 amount)
    {
        return processContribution();
    }

     /**
        @dev Contribution during presale (min 200 ether)
        can only be called 14 days before the crowdsale start date

        @return tokens issued in return
    */
    function contributePreSale()
        public
        payable
        between(safeSub(startTime,PRESALE_DURATION), startTime)
        validatePresaleMinPrice
        validatePresaleAddress
        returns (uint256 amount)
    {
        return processContribution();
    }

    /**
        @dev handles contribution logic
        note that the Contribution event is triggered using the sender as the contributor, regardless of the actual contributor

        @return tokens issued in return
    */
    function processContribution() private
        active
        etherCapNotReached(msg.value)
        validGasPrice
        returns (uint256 amount)
    {
        uint256 tokenAmount = computeReturn(msg.value);
        assert(beneficiary.send(msg.value)); // transfer the ether to the beneficiary account
        totalEtherContributed = safeAdd(totalEtherContributed, msg.value); // update the total contribution amount
        token.issue(msg.sender, tokenAmount); // issue new funds to the contributor in the smart token
        token.issue(beneficiary, tokenAmount); // issue tokens to the beneficiary

        Contribution(msg.sender, msg.value, tokenAmount);
        return tokenAmount;
    }

    // fallback
    function() payable {
        contributeETH();
    }
}


Rework solidity, set more variables to public

pragma solidity ^0.4.11;

contract RockPaperScissors {

    enum Stages {
        playerOneThrow,
        playerTwoThrow,
        playerOneReveal,
        playerTwoReveal,
        determineWinner
    }

    Stages public stage = Stages.playerOneThrow;

    modifier atStage(Stages _stage) {
        require(stage == _stage);
        _;
    }

    function nextStage() internal {
        stage = Stages(uint(stage) + 1);
    }

    Player public player1;
    Player public player2;
    uint public bet = 0;
    uint public resetTime;
    address public winner;

    struct Player{
        address addy;
        bytes32 commitHash;
        uint commitTime;
        uint gameThrow;
        uint revealTime;
    }

    function RockPaperScissors(address _player1, address _player2, uint _bet)  {
        player1.addy = _player1;
        player2.addy = _player2;
        bet = _bet;
    }

    modifier includesPayment(){if(msg.value == bet){ _; }else{ throw; } }

    function playerOneCommit(bytes32 commitHash) payable atStage(Stages.playerOneThrow) includesPayment() {
        if(msg.sender == player1.addy) {
            player1.commitHash = commitHash;
            player1.commitTime = now;
            resetTime = 0;
            nextStage();
        } else {
            throw;
        }
    }

    function playerTwoCommit(bytes32 commitHash) payable atStage(Stages.playerTwoThrow) includesPayment() {
        if(msg.sender == player2.addy) {
            player2.commitHash = commitHash;
            player2.commitTime = now;
            nextStage();
        } else {
            throw;
        }
    }

    function playerOneReveal(uint gameThrow, uint secret) atStage(Stages.playerOneReveal) {
        bytes32 commitHash = sha3(gameThrow, secret);
        if(commitHash == player1.commitHash) {
            player1.gameThrow = gameThrow;
            player1.revealTime = now;
            nextStage();
        } else {
            throw;
        }
    }

    function playerTwoReveal(uint gameThrow, uint secret) atStage(Stages.playerTwoReveal) {
        bytes32 commitHash = sha3(gameThrow, secret);
        if(commitHash == player2.commitHash) {
            player2.gameThrow = gameThrow;
            player2.revealTime = now;
            nextStage();
            determineWinner();
        } else {
            throw;
        }
    }

    function determineWinner() atStage(Stages.determineWinner) {

        if(player1.gameThrow == 1 && player2.gameThrow == 2) {
            winner = player2.addy;
        } else if(player1.gameThrow == 2 && player2.gameThrow == 3) {
            winner = player2.addy;
        } else if(player1.gameThrow == 3 && player2.gameThrow == 1) {
            winner = player2.addy;
        } else if (player1.gameThrow != player2.gameThrow) {
            winner = player1.addy;
        }

        if(winner != 0x0) {
            winner.transfer(this.balance);
        } else { //tie
            reset();
        }
    }

    function returnFunds(){
        if(stage == Stages.playerOneThrow){
            if(player1.commitHash == 0 && resetTime + 3600 > now){
                splitFunds();
            }
        }
        if(stage == Stages.playerTwoThrow){
            if(player1.commitTime + 3600 > now){
                player1.addy.transfer(this.balance);
            }
        }
        if(stage == Stages.playerOneReveal){
            if(player2.commitTime + 3600 > now){
                player2.addy.transfer(this.balance); // or splitFunds() ?
            }
        }
        if(stage == Stages.playerTwoReveal){
            if(player1.revealTime + 3600 > now){
                player1.addy.transfer(this.balance);
            }
        }
    }

    function splitFunds(){
        player1.addy.transfer(this.balance/2);
        player2.addy.transfer(this.balance/2);
    }

    function reset() private{
        delete player1.commitHash;
        delete player1.gameThrow;
        delete player1.commitTime;
        delete player1.revealTime;
        delete player2.commitHash;
        delete player2.gameThrow;
        delete player2.commitTime;
        delete player2.revealTime;
        resetTime = now;
        stage = Stages.playerOneThrow;
    }
}

pragma solidity ^0.4.11;

contract RockPaperScissors {

    enum Stages {
        playerOneThrow,
        playerTwoThrow,
        playerOneReveal,
        playerTwoReveal,
        determineWinner
    }

    Stages public stage = Stages.playerOneThrow;

    modifier atStage(Stages _stage) {
        require(stage == _stage);
        _;
    }

    function nextStage() internal {
        stage = Stages(uint(stage) + 1);
    }

    Player player1;
    Player player2;
    uint bet;
    uint public resetTime;

    struct Player{
        address addy;
        bytes32 commitHash;
        uint commitTime;
        uint gameThrow;
        uint revealTime;
    }

    function RockPaperScissors(address _player1, address _player2, uint _bet)  {
        player1.addy = _player1;
        player2.addy = _player2;
        bet = _bet;
    }

    modifier includesPayment(){if(msg.value == bet){ _; }else{ throw; } }

    function playerOneCommit(bytes32 commitHash) payable atStage(Stages.playerOneThrow) includesPayment() {
        if(msg.sender == player1.addy) {
            player1.commitHash = commitHash;
            player1.commitTime = now;
            resetTime = 0;
            nextStage();
        } else {
            throw;
        }
    }

    function playerTwoCommit(bytes32 commitHash) payable atStage(Stages.playerTwoThrow) includesPayment() {
        if(msg.sender == player2.addy) {
            player2.commitHash = commitHash;
            player2.commitTime = now;
            nextStage();
        } else {
            throw;
        }
    }

    function playerOneReveal(uint gameThrow, uint secret) atStage(Stages.playerOneReveal) {
        bytes32 commitHash = sha3(gameThrow, secret);
        if(commitHash == player1.commitHash) {
            player1.gameThrow = gameThrow;
            player1.revealTime = now;
            nextStage();
        } else {
            throw;
        }
    }

    function playerTwoReveal(uint gameThrow, uint secret) atStage(Stages.playerTwoReveal) {
        bytes32 commitHash = sha3(gameThrow, secret);
        if(commitHash == player2.commitHash) {
            player2.gameThrow = gameThrow;
            player2.revealTime = now;
            nextStage();
        } else {
            throw;
        }
    }

    function determineWinner() atStage(Stages.determineWinner) {
        address winner;
        if(player1.gameThrow == 1 && player2.gameThrow == 2) {
            winner = player2.addy;
        } else if(player1.gameThrow == 2 && player2.gameThrow == 3) {
            winner = player2.addy;
        } else if(player1.gameThrow == 3 && player2.gameThrow == 1) {
            winner = player2.addy;
        } else if (player1.gameThrow != player2.gameThrow) {
            winner = player1.addy;
        }

        if(winner != 0x0) {
            winner.transfer(this.balance);
            reset();
        } else { //tie
            splitFunds();
        }
    }

    function returnFunds(){
        if(stage == Stages.playerOneThrow){
            if(player1.commitHash == 0 && resetTime + 3600 > now){
                splitFunds();
            }
        }
        if(stage == Stages.playerTwoThrow){
            if(player1.commitTime + 3600 > now){
                player1.addy.transfer(this.balance);
            }
        }
        if(stage == Stages.playerOneReveal){
            if(player2.commitTime + 3600 > now){
                player2.addy.transfer(this.balance); // or splitFunds() ?
            }
        }
        if(stage == Stages.playerTwoReveal){
            if(player1.revealTime + 3600 > now){
                player1.addy.transfer(this.balance);
            }
        }
    }

    function splitFunds(){
        player1.addy.transfer(this.balance/2);
        player2.addy.transfer(this.balance/2);
    }

    function reset() private{
        delete player1.commitHash;
        delete player1.gameThrow;
        delete player1.commitTime;
        delete player1.revealTime;
        delete player2.commitHash;
        delete player2.gameThrow;
        delete player2.commitTime;
        delete player2.revealTime;
        resetTime = now;
        stage = Stages.playerOneThrow;
    }
}