added failures + struct for multicall

added failures + struct for multicall

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 <0.9.0;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {LowLevelCallUtils} from "./LowLevelCallUtils.sol";
import {ENS} from "../registry/ENS.sol";
import {IExtendedResolver} from "../resolvers/profiles/IExtendedResolver.sol";
import {Resolver, INameResolver, IAddrResolver} from "../resolvers/Resolver.sol";
import {NameEncoder} from "./NameEncoder.sol";
import {BytesUtils} from "../wrapper/BytesUtils.sol";

error OffchainLookup(
    address sender,
    string[] urls,
    bytes callData,
    bytes4 callbackFunction,
    bytes extraData
);

struct MulticallData {
    bytes name;
    bytes[] data;
    string[] gateways;
    bytes4 callbackFunction;
    address resolver;
    bool shouldEncode;
    bytes metaData;
    bool[] failures;
}

struct OffchainLookupCallData {
    address sender;
    string[] urls;
    bytes callData;
}

struct OffchainLookupExtraData {
    bytes4 callbackFunction;
    bytes data;
}

interface BatchGateway {
    function query(OffchainLookupCallData[] memory data)
        external
        returns (bool[] memory failures, bytes[] memory responses);
}

/**
 * The Universal Resolver is a contract that handles the work of resolving a name entirely onchain,
 * making it possible to make a single smart contract call to resolve an ENS name.
 */
contract UniversalResolver is ERC165, Ownable {
    using Address for address;
    using NameEncoder for string;
    using BytesUtils for bytes;

    string[] public batchGatewayURLs;
    ENS public immutable registry;

    constructor(address _registry, string[] memory _urls) {
        registry = ENS(_registry);
        batchGatewayURLs = _urls;
    }

    function setGatewayURLs(string[] memory _urls) public onlyOwner {
        batchGatewayURLs = _urls;
    }

    /**
     * @dev Performs ENS name resolution for the supplied name and resolution data.
     * @param name The name to resolve, in normalised and DNS-encoded form.
     * @param data The resolution data, as specified in ENSIP-10.
     * @return The result of resolving the name.
     */
    function resolve(bytes calldata name, bytes memory data)
        external
        view
        returns (bytes memory, address)
    {
        return
            _resolveSingle(
                name,
                data,
                batchGatewayURLs,
                this.resolveSingleCallback.selector,
                ""
            );
    }

    function resolve(bytes calldata name, bytes[] memory data)
        external
        view
        returns (bytes[] memory, address)
    {
        return resolve(name, data, batchGatewayURLs);
    }

    function resolve(
        bytes calldata name,
        bytes memory data,
        string[] memory gateways
    ) external view returns (bytes memory, address) {
        return
            _resolveSingle(
                name,
                data,
                gateways,
                this.resolveSingleCallback.selector,
                ""
            );
    }

    function resolve(
        bytes calldata name,
        bytes[] memory data,
        string[] memory gateways
    ) public view returns (bytes[] memory, address) {
        return
            _resolve(name, data, gateways, this.resolveCallback.selector, "");
    }

    function _resolveSingle(
        bytes calldata name,
        bytes memory data,
        string[] memory gateways,
        bytes4 callbackFunction,
        bytes memory metaData
    ) public view returns (bytes memory, address) {
        bytes[] memory dataArr = new bytes[](1);
        dataArr[0] = data;
        (bytes[] memory results, address resolver) = _resolve(
            name,
            dataArr,
            gateways,
            callbackFunction,
            metaData
        );
        return (results[0], resolver);
    }

    function _resolve(
        bytes calldata name,
        bytes[] memory data,
        string[] memory gateways,
        bytes4 callbackFunction,
        bytes memory metaData
    ) internal view returns (bytes[] memory results, address resolverAddress) {
        (Resolver resolver, ) = findResolver(name);
        resolverAddress = address(resolver);
        if (resolverAddress == address(0)) {
            return (results, address(0));
        }

        bool hasExtendedResolver = false;

        try
            resolver.supportsInterface(type(IExtendedResolver).interfaceId)
        returns (bool supported) {
            hasExtendedResolver = supported;
        } catch {}

        results = _multicall(
            MulticallData(
                name,
                data,
                gateways,
                callbackFunction,
                resolverAddress,
                hasExtendedResolver,
                metaData,
                new bool[](data.length)
            )
        );
    }

    function reverse(bytes calldata reverseName)
        external
        view
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        return reverse(reverseName, batchGatewayURLs);
    }

    /**
     * @dev Performs ENS name reverse resolution for the supplied reverse name.
     * @param reverseName The reverse name to resolve, in normalised and DNS-encoded form. e.g. b6E040C9ECAaE172a89bD561c5F73e1C48d28cd9.addr.reverse
     * @return The resolved name, the resolved address, the reverse resolver address, and the resolver address.
     */
    function reverse(bytes calldata reverseName, string[] memory gateways)
        public
        view
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        bytes memory encodedCall = abi.encodeCall(
            INameResolver.name,
            reverseName.namehash(0)
        );
        (
            bytes memory resolvedReverseData,
            address reverseResolverAddress
        ) = _resolveSingle(
                reverseName,
                encodedCall,
                gateways,
                this.reverseCallback.selector,
                ""
            );

        return
            getForwardDataFromReverse(
                resolvedReverseData,
                reverseResolverAddress,
                gateways
            );
    }

    function getForwardDataFromReverse(
        bytes memory resolvedReverseData,
        address reverseResolverAddress,
        string[] memory gateways
    )
        internal
        view
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        string memory resolvedName = abi.decode(resolvedReverseData, (string));

        (bytes memory encodedName, bytes32 namehash) = resolvedName
            .dnsEncodeName();

        bytes memory encodedCall = abi.encodeCall(IAddrResolver.addr, namehash);
        bytes memory metaData = abi.encode(
            resolvedReverseData,
            reverseResolverAddress
        );
        (bytes memory resolvedData, address resolverAddress) = this
            ._resolveSingle(
                encodedName,
                encodedCall,
                gateways,
                this.reverseCallback.selector,
                metaData
            );

        address resolvedAddress = abi.decode(resolvedData, (address));

        return (
            resolvedName,
            resolvedAddress,
            reverseResolverAddress,
            resolverAddress
        );
    }

    function resolveSingleCallback(
        bytes calldata response,
        bytes calldata extraData
    ) external virtual returns (bytes memory, address) {
        (bytes[] memory results, address resolver, , ) = _resolveCallback(
            response,
            extraData,
            this.resolveSingleCallback.selector
        );
        return (results[0], resolver);
    }

    function resolveCallback(bytes calldata response, bytes calldata extraData)
        external
        virtual
        returns (bytes[] memory, address)
    {
        (bytes[] memory results, address resolver, , ) = _resolveCallback(
            response,
            extraData,
            this.resolveCallback.selector
        );
        return (results, resolver);
    }

    function reverseCallback(bytes calldata response, bytes calldata extraData)
        external
        virtual
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        (
            bytes[] memory resolvedData,
            address resolverAddress,
            string[] memory gateways,
            bytes memory metaData
        ) = _resolveCallback(
                response,
                extraData,
                this.reverseCallback.selector
            );

        if (metaData.length > 0) {
            (string memory resolvedName, address reverseResolverAddress) = abi
                .decode(metaData, (string, address));
            address resolvedAddress = abi.decode(resolvedData[0], (address));
            return (
                resolvedName,
                resolvedAddress,
                reverseResolverAddress,
                resolverAddress
            );
        }

        return
            getForwardDataFromReverse(
                resolvedData[0],
                resolverAddress,
                gateways
            );
    }

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

    function _resolveCallback(
        bytes calldata response,
        bytes calldata extraData,
        bytes4 callbackFunction
    )
        internal
        virtual
        returns (
            bytes[] memory,
            address,
            string[] memory,
            bytes memory
        )
    {
        MulticallData memory multicallData;
        multicallData.callbackFunction = callbackFunction;
        bytes[] memory responses;
        (multicallData.failures, responses) = abi.decode(
            response,
            (bool[], bytes[])
        );
        OffchainLookupExtraData[] memory extraDatas;
        (
            multicallData.resolver,
            multicallData.gateways,
            multicallData.metaData,
            extraDatas
        ) = abi.decode(
            extraData,
            (address, string[], bytes, OffchainLookupExtraData[])
        );
        require(responses.length <= extraDatas.length);
        multicallData.data = new bytes[](extraDatas.length);

        uint256 offchainCount = 0;
        for (uint256 i = 0; i < extraDatas.length; i++) {
            if (extraDatas[i].callbackFunction == bytes4(0)) {
                // This call did not require an offchain lookup; use the previous input data.
                multicallData.data[i] = extraDatas[i].data;
            } else {
                if (multicallData.failures[i]) {
                    multicallData.data[i] = responses[offchainCount];
                } else {
                    multicallData.data[i] = abi.encodeWithSelector(
                        extraDatas[i].callbackFunction,
                        responses[offchainCount],
                        extraDatas[i].data
                    );
                }
                offchainCount = offchainCount + 1;
            }
        }

        return (
            _multicall(multicallData),
            multicallData.resolver,
            multicallData.gateways,
            multicallData.metaData
        );
    }

    /**
     * @dev Makes a call to `target` with `data`. If the call reverts with an `OffchainLookup` error, wraps
     *      the error with the data necessary to continue the request where it left off.
     * @param target The address to call.
     * @param data The data to call `target` with.
     * @return offchain Whether the call reverted with an `OffchainLookup` error.
     * @return returnData If `target` did not revert, contains the return data from the call to `target`. Otherwise, contains a `OffchainLookupCallData` struct.
     * @return extraData If `target` did not revert, is empty. Otherwise, contains a `OffchainLookupExtraData` struct.
     */
    function callWithOffchainLookupPropagation(
        address target,
        bytes memory data
    )
        internal
        view
        returns (
            bool offchain,
            bytes memory returnData,
            OffchainLookupExtraData memory extraData
        )
    {
        bool result = LowLevelCallUtils.functionStaticCall(
            address(target),
            data
        );
        uint256 size = LowLevelCallUtils.returnDataSize();

        if (result) {
            return (
                false,
                LowLevelCallUtils.readReturnData(0, size),
                extraData
            );
        }

        // Failure
        if (size >= 4) {
            bytes memory errorId = LowLevelCallUtils.readReturnData(0, 4);
            if (bytes4(errorId) == OffchainLookup.selector) {
                // Offchain lookup. Decode the revert message and create our own that nests it.
                bytes memory revertData = LowLevelCallUtils.readReturnData(
                    4,
                    size - 4
                );
                (
                    address wrappedSender,
                    string[] memory wrappedUrls,
                    bytes memory wrappedCallData,
                    bytes4 wrappedCallbackFunction,
                    bytes memory wrappedExtraData
                ) = abi.decode(
                        revertData,
                        (address, string[], bytes, bytes4, bytes)
                    );
                if (wrappedSender == target) {
                    returnData = abi.encode(
                        OffchainLookupCallData(
                            wrappedSender,
                            wrappedUrls,
                            wrappedCallData
                        )
                    );
                    extraData = OffchainLookupExtraData(
                        wrappedCallbackFunction,
                        wrappedExtraData
                    );
                    return (true, returnData, extraData);
                }
            }
        }
    }

    /**
     * @dev Finds a resolver by recursively querying the registry, starting at the longest name and progressively
     *      removing labels until it finds a result.
     * @param name The name to resolve, in DNS-encoded and normalised form.
     * @return The Resolver responsible for this name, and the namehash of the full name.
     */
    function findResolver(bytes calldata name)
        public
        view
        returns (Resolver, bytes32)
    {
        (address resolver, bytes32 labelhash) = findResolver(name, 0);
        return (Resolver(resolver), labelhash);
    }

    function findResolver(bytes calldata name, uint256 offset)
        internal
        view
        returns (address, bytes32)
    {
        uint256 labelLength = uint256(uint8(name[offset]));
        if (labelLength == 0) {
            return (address(0), bytes32(0));
        }
        uint256 nextLabel = offset + labelLength + 1;
        bytes32 labelHash = keccak256(name[offset + 1:nextLabel]);
        (address parentresolver, bytes32 parentnode) = findResolver(
            name,
            nextLabel
        );
        bytes32 node = keccak256(abi.encodePacked(parentnode, labelHash));
        address resolver = registry.resolver(node);
        if (resolver != address(0)) {
            return (resolver, node);
        }
        return (parentresolver, node);
    }

    function _multicall(MulticallData memory multicallData)
        internal
        view
        returns (bytes[] memory results)
    {
        uint256 length = multicallData.data.length;
        uint256 offchainCount = 0;
        OffchainLookupCallData[]
            memory callDatas = new OffchainLookupCallData[](length);
        OffchainLookupExtraData[]
            memory extraDatas = new OffchainLookupExtraData[](length);
        results = new bytes[](length);
        bool shouldDecode = multicallData.name.length == 0;

        for (uint256 i = 0; i < length; i++) {
            bytes memory eData = multicallData.data[i];
            bytes memory item = multicallData.data[i];
            bool failure = multicallData.failures[i];
            if (failure) {
                results[i] = multicallData.data[i];
                continue;
            }
            if (multicallData.shouldEncode) {
                item = abi.encodeCall(
                    IExtendedResolver.resolve,
                    (multicallData.name, item)
                );
            }
            (
                bool offchain,
                bytes memory returnData,
                OffchainLookupExtraData memory extraData
            ) = callWithOffchainLookupPropagation(multicallData.resolver, item);

            if (offchain) {
                callDatas[offchainCount] = abi.decode(
                    returnData,
                    (OffchainLookupCallData)
                );
                extraDatas[i] = extraData;
                offchainCount += 1;
                continue;
            }

            if (shouldDecode) {
                results[i] = abi.decode(returnData, (bytes));
            } else {
                // if name is empty, this is a callback request so we should decode the result
                results[i] = returnData;
            }
            extraDatas[i].data = eData;
        }

        if (offchainCount == 0) {
            return results;
        }

        // Trim callDatas if offchain data exists
        assembly {
            mstore(callDatas, offchainCount)
        }

        revert OffchainLookup(
            address(this),
            multicallData.gateways,
            abi.encodeWithSelector(BatchGateway.query.selector, callDatas),
            multicallData.callbackFunction,
            abi.encode(
                multicallData.resolver,
                multicallData.gateways,
                multicallData.metaData,
                extraDatas
            )
        );
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 <0.9.0;

import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {LowLevelCallUtils} from "./LowLevelCallUtils.sol";
import {ENS} from "../registry/ENS.sol";
import {IExtendedResolver} from "../resolvers/profiles/IExtendedResolver.sol";
import {Resolver, INameResolver, IAddrResolver} from "../resolvers/Resolver.sol";
import {NameEncoder} from "./NameEncoder.sol";
import {BytesUtils} from "../wrapper/BytesUtils.sol";

error OffchainLookup(
    address sender,
    string[] urls,
    bytes callData,
    bytes4 callbackFunction,
    bytes extraData
);

struct OffchainLookupCallData {
    address sender;
    string[] urls;
    bytes callData;
}

struct OffchainLookupExtraData {
    bytes4 callbackFunction;
    bytes data;
}

interface BatchGateway {
    function query(OffchainLookupCallData[] memory data)
        external
        returns (bytes[] memory responses);
}

/**
 * The Universal Resolver is a contract that handles the work of resolving a name entirely onchain,
 * making it possible to make a single smart contract call to resolve an ENS name.
 */
contract UniversalResolver is ERC165, Ownable {
    using Address for address;
    using NameEncoder for string;
    using BytesUtils for bytes;

    string[] public batchGatewayURLs;
    ENS public immutable registry;

    constructor(address _registry, string[] memory _urls) {
        registry = ENS(_registry);
        batchGatewayURLs = _urls;
    }

    function setGatewayURLs(string[] memory _urls) public onlyOwner {
        batchGatewayURLs = _urls;
    }

    /**
     * @dev Performs ENS name resolution for the supplied name and resolution data.
     * @param name The name to resolve, in normalised and DNS-encoded form.
     * @param data The resolution data, as specified in ENSIP-10.
     * @return The result of resolving the name.
     */
    function resolve(bytes calldata name, bytes memory data)
        external
        view
        returns (bytes memory, address)
    {
        return
            _resolveSingle(
                name,
                data,
                batchGatewayURLs,
                this.resolveSingleCallback.selector,
                ""
            );
    }

    function resolve(bytes calldata name, bytes[] memory data)
        external
        view
        returns (bytes[] memory, address)
    {
        return resolve(name, data, batchGatewayURLs);
    }

    function resolve(
        bytes calldata name,
        bytes memory data,
        string[] memory gateways
    ) external view returns (bytes memory, address) {
        return
            _resolveSingle(
                name,
                data,
                gateways,
                this.resolveSingleCallback.selector,
                ""
            );
    }

    function resolve(
        bytes calldata name,
        bytes[] memory data,
        string[] memory gateways
    ) public view returns (bytes[] memory, address) {
        return
            _resolve(name, data, gateways, this.resolveCallback.selector, "");
    }

    function _resolveSingle(
        bytes calldata name,
        bytes memory data,
        string[] memory gateways,
        bytes4 callbackFunction,
        bytes memory metaData
    ) public view returns (bytes memory, address) {
        bytes[] memory dataArr = new bytes[](1);
        dataArr[0] = data;
        (bytes[] memory results, address resolver) = _resolve(
            name,
            dataArr,
            gateways,
            callbackFunction,
            metaData
        );
        return (results[0], resolver);
    }

    function _resolve(
        bytes calldata name,
        bytes[] memory data,
        string[] memory gateways,
        bytes4 callbackFunction,
        bytes memory metaData
    ) internal view returns (bytes[] memory results, address resolverAddress) {
        (Resolver resolver, ) = findResolver(name);
        resolverAddress = address(resolver);
        if (resolverAddress == address(0)) {
            return (results, address(0));
        }

        bool hasExtendedResolver = false;

        try
            resolver.supportsInterface(type(IExtendedResolver).interfaceId)
        returns (bool supported) {
            hasExtendedResolver = supported;
        } catch {}

        results = _multicall(
            name,
            data,
            gateways,
            callbackFunction,
            resolverAddress,
            hasExtendedResolver,
            metaData
        );
    }

    function reverse(bytes calldata reverseName)
        external
        view
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        return reverse(reverseName, batchGatewayURLs);
    }

    /**
     * @dev Performs ENS name reverse resolution for the supplied reverse name.
     * @param reverseName The reverse name to resolve, in normalised and DNS-encoded form. e.g. b6E040C9ECAaE172a89bD561c5F73e1C48d28cd9.addr.reverse
     * @return The resolved name, the resolved address, the reverse resolver address, and the resolver address.
     */
    function reverse(bytes calldata reverseName, string[] memory gateways)
        public
        view
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        bytes memory encodedCall = abi.encodeCall(
            INameResolver.name,
            reverseName.namehash(0)
        );
        (
            bytes memory resolvedReverseData,
            address reverseResolverAddress
        ) = _resolveSingle(
                reverseName,
                encodedCall,
                gateways,
                this.reverseCallback.selector,
                ""
            );

        return
            getForwardDataFromReverse(
                resolvedReverseData,
                reverseResolverAddress,
                gateways
            );
    }

    function getForwardDataFromReverse(
        bytes memory resolvedReverseData,
        address reverseResolverAddress,
        string[] memory gateways
    )
        internal
        view
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        string memory resolvedName = abi.decode(resolvedReverseData, (string));

        (bytes memory encodedName, bytes32 namehash) = resolvedName
            .dnsEncodeName();

        bytes memory encodedCall = abi.encodeCall(IAddrResolver.addr, namehash);
        bytes memory metaData = abi.encode(
            resolvedReverseData,
            reverseResolverAddress
        );
        (bytes memory resolvedData, address resolverAddress) = this
            ._resolveSingle(
                encodedName,
                encodedCall,
                gateways,
                this.reverseCallback.selector,
                metaData
            );

        address resolvedAddress = abi.decode(resolvedData, (address));

        return (
            resolvedName,
            resolvedAddress,
            reverseResolverAddress,
            resolverAddress
        );
    }

    function resolveSingleCallback(
        bytes calldata response,
        bytes calldata extraData
    ) external virtual returns (bytes memory, address) {
        (bytes[] memory results, address resolver, , ) = _resolveCallback(
            response,
            extraData,
            this.resolveSingleCallback.selector
        );
        return (results[0], resolver);
    }

    function resolveCallback(bytes calldata response, bytes calldata extraData)
        external
        virtual
        returns (bytes[] memory, address)
    {
        (bytes[] memory results, address resolver, , ) = _resolveCallback(
            response,
            extraData,
            this.resolveCallback.selector
        );
        return (results, resolver);
    }

    function reverseCallback(bytes calldata response, bytes calldata extraData)
        external
        virtual
        returns (
            string memory,
            address,
            address,
            address
        )
    {
        (
            bytes[] memory resolvedData,
            address resolverAddress,
            string[] memory gateways,
            bytes memory metaData
        ) = _resolveCallback(
                response,
                extraData,
                this.reverseCallback.selector
            );

        if (metaData.length > 0) {
            (string memory resolvedName, address reverseResolverAddress) = abi
                .decode(metaData, (string, address));
            address resolvedAddress = abi.decode(resolvedData[0], (address));
            return (
                resolvedName,
                resolvedAddress,
                reverseResolverAddress,
                resolverAddress
            );
        }

        return
            getForwardDataFromReverse(
                resolvedData[0],
                resolverAddress,
                gateways
            );
    }

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

    function _resolveCallback(
        bytes calldata response,
        bytes calldata extraData,
        bytes4 callbackFunction
    )
        internal
        virtual
        returns (
            bytes[] memory,
            address,
            string[] memory,
            bytes memory
        )
    {
        bytes[] memory responses = abi.decode(response, (bytes[]));
        (
            address resolver,
            string[] memory gateways,
            bytes memory metaData,
            OffchainLookupExtraData[] memory extraDatas
        ) = abi.decode(
                extraData,
                (address, string[], bytes, OffchainLookupExtraData[])
            );
        require(responses.length <= extraDatas.length);
        bytes[] memory data = new bytes[](extraDatas.length);

        uint256 offchainCount = 0;
        for (uint256 i = 0; i < extraDatas.length; i++) {
            if (extraDatas[i].callbackFunction == bytes4(0)) {
                // This call did not require an offchain lookup; use the previous input data.
                data[i] = extraDatas[i].data;
            } else {
                // Encode the callback as another multicall
                data[i] = abi.encodeWithSelector(
                    extraDatas[i].callbackFunction,
                    responses[offchainCount],
                    extraDatas[i].data
                );
                offchainCount = offchainCount + 1;
            }
        }
        return (
            _multicall(
                new bytes(0),
                data,
                gateways,
                callbackFunction,
                resolver,
                false,
                metaData
            ),
            resolver,
            gateways,
            metaData
        );
    }

    /**
     * @dev Makes a call to `target` with `data`. If the call reverts with an `OffchainLookup` error, wraps
     *      the error with the data necessary to continue the request where it left off.
     * @param target The address to call.
     * @param data The data to call `target` with.
     * @return offchain Whether the call reverted with an `OffchainLookup` error.
     * @return returnData If `target` did not revert, contains the return data from the call to `target`. Otherwise, contains a `OffchainLookupCallData` struct.
     * @return extraData If `target` did not revert, is empty. Otherwise, contains a `OffchainLookupExtraData` struct.
     */
    function callWithOffchainLookupPropagation(
        address target,
        bytes memory data
    )
        internal
        view
        returns (
            bool offchain,
            bytes memory returnData,
            OffchainLookupExtraData memory extraData
        )
    {
        bool result = LowLevelCallUtils.functionStaticCall(
            address(target),
            data
        );
        uint256 size = LowLevelCallUtils.returnDataSize();

        if (result) {
            return (
                false,
                LowLevelCallUtils.readReturnData(0, size),
                extraData
            );
        }

        // Failure
        if (size >= 4) {
            bytes memory errorId = LowLevelCallUtils.readReturnData(0, 4);
            if (bytes4(errorId) == OffchainLookup.selector) {
                // Offchain lookup. Decode the revert message and create our own that nests it.
                bytes memory revertData = LowLevelCallUtils.readReturnData(
                    4,
                    size - 4
                );
                (
                    address wrappedSender,
                    string[] memory wrappedUrls,
                    bytes memory wrappedCallData,
                    bytes4 wrappedCallbackFunction,
                    bytes memory wrappedExtraData
                ) = abi.decode(
                        revertData,
                        (address, string[], bytes, bytes4, bytes)
                    );
                if (wrappedSender == target) {
                    returnData = abi.encode(
                        OffchainLookupCallData(
                            wrappedSender,
                            wrappedUrls,
                            wrappedCallData
                        )
                    );
                    extraData = OffchainLookupExtraData(
                        wrappedCallbackFunction,
                        wrappedExtraData
                    );
                    return (true, returnData, extraData);
                }
            }
        }

        LowLevelCallUtils.propagateRevert();
    }

    /**
     * @dev Finds a resolver by recursively querying the registry, starting at the longest name and progressively
     *      removing labels until it finds a result.
     * @param name The name to resolve, in DNS-encoded and normalised form.
     * @return The Resolver responsible for this name, and the namehash of the full name.
     */
    function findResolver(bytes calldata name)
        public
        view
        returns (Resolver, bytes32)
    {
        (address resolver, bytes32 labelhash) = findResolver(name, 0);
        return (Resolver(resolver), labelhash);
    }

    function findResolver(bytes calldata name, uint256 offset)
        internal
        view
        returns (address, bytes32)
    {
        uint256 labelLength = uint256(uint8(name[offset]));
        if (labelLength == 0) {
            return (address(0), bytes32(0));
        }
        uint256 nextLabel = offset + labelLength + 1;
        bytes32 labelHash = keccak256(name[offset + 1:nextLabel]);
        (address parentresolver, bytes32 parentnode) = findResolver(
            name,
            nextLabel
        );
        bytes32 node = keccak256(abi.encodePacked(parentnode, labelHash));
        address resolver = registry.resolver(node);
        if (resolver != address(0)) {
            return (resolver, node);
        }
        return (parentresolver, node);
    }

    function _multicall(
        bytes memory name,
        bytes[] memory data,
        string[] memory gateways,
        bytes4 callbackFunction,
        address resolver,
        bool shouldEncode,
        bytes memory metaData
    ) internal view returns (bytes[] memory results) {
        uint256 length = data.length;
        uint256 offchainCount = 0;
        OffchainLookupCallData[]
            memory callDatas = new OffchainLookupCallData[](length);
        OffchainLookupExtraData[]
            memory extraDatas = new OffchainLookupExtraData[](length);
        results = new bytes[](length);
        bool shouldDecode = name.length == 0;

        for (uint256 i = 0; i < length; i++) {
            bytes memory eData = data[i];
            bytes memory item = data[i];
            if (shouldEncode) {
                item = abi.encodeCall(IExtendedResolver.resolve, (name, item));
            }
            (
                bool offchain,
                bytes memory returnData,
                OffchainLookupExtraData memory extraData
            ) = callWithOffchainLookupPropagation(resolver, item);

            if (offchain) {
                callDatas[offchainCount] = abi.decode(
                    returnData,
                    (OffchainLookupCallData)
                );
                extraDatas[i] = extraData;
                offchainCount += 1;
                continue;
            }

            if (shouldDecode) {
                results[i] = abi.decode(returnData, (bytes));
            } else {
                // if name is empty, this is a callback request so we should decode the result
                results[i] = returnData;
            }
            extraDatas[i].data = eData;
        }

        if (offchainCount == 0) {
            return results;
        }

        // Trim callDatas if offchain data exists
        assembly {
            mstore(callDatas, offchainCount)
        }

        revert OffchainLookup(
            address(this),
            gateways,
            abi.encodeWithSelector(BatchGateway.query.selector, callDatas),
            callbackFunction,
            abi.encode(resolver, gateways, metaData, extraDatas)
        );
    }
}


remove timelimit to confirm

remove timelimit to confirm

pragma solidity ^0.4.0;

import "./Math.sol";
import "./Concept.sol";
import "./ConceptRegistry.sol";
import "./UserRegistry.sol";

contract Assessment {
  address assessee; //The address of the user being assessed
  address[] assessors;
  address[] finalAssessors; //The assessors who actually give an assessment
  mapping (address => State) assessorState;
  mapping(uint => int[]) clusters; //Initializes mapping to store clusters of scores
  State assessmentStage;
  enum State {//The state of the assessors
    None,
    Called, //Been called as a potential assessor
    Confirmed, //Confirmed as assessing
    Committed, //Committed a score
    Done, //Completed the assessment processs
    Burned //Entire stake has been burned
  }

  uint public assessorPoolLength; //how many users are potential assessors (also changed to public)
  address concept;
  address userRegistry;
  address conceptRegistry;
  uint public startTime;
  uint public size;
  uint cost;
  mapping(address => string[]) public data; //IPFS hashes of data that can be passed between the assessors and the assessee for the assessment to occur
  mapping(address => bytes32) commits;
  mapping(address => uint) stake;
  uint done = 0;
  mapping(address => int8) scores; //could save gas by making this an int8
  mapping(int => bool) inRewardCluster;
  int finalScore;
  event DataSet(address _dataSetter, uint _index);

  modifier onlyAssessorAssessee() {
    if(msg.sender != assessee && uint(assessorState[msg.sender]) == 0) {
      throw;
    }
    _;
  }

  modifier onlyConceptAssessment() {
    if(msg.sender != address(this) && msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyConcept() {
    if(msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyInStage(State _stage) {
      if(assessmentStage != _stage){
          throw;
      }
      _;
  }

  function Assessment(address _assessee, address _userRegistry, address _conceptRegistry, uint _size, uint _cost) {
    assessee = _assessee;
    concept = msg.sender;
    userRegistry = _userRegistry;
    conceptRegistry = _conceptRegistry;
    startTime = block.number;
    size = _size;
    cost = _cost;
    UserRegistry(userRegistry).notification(assessee, 0); // assesse has started an assessment
    assessorPoolLength = 0;
  }

  function cancelAssessment() onlyInStage(State.Called) {
    if(msg.sender != assessee) throw;
    Concept(concept).addBalance(assessee, cost*size);
    UserRegistry(userRegistry).notification(assessee, 3); //Assessment Cancled and you have been refunded
    for(uint i = 0; i < assessors.length; i++) {
      Concept(concept).addBalance(assessors[i], cost);
      UserRegistry(userRegistry).notification(assessors[i], 3); //Assessment Cancled and you have been refunded
    }
    suicide(conceptRegistry);
  }

  //@purpose: adds a user to the pool eligible to accept an assessment
  function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
    if(assessorState[assessor] == State.None) {
      UserRegistry(userRegistry).notification(assessor, 1); //Called As A Potential Assessor
      assessorState[assessor] = State.Called; //Sets the state of the assessor as called
      assessorPoolLength++;
      return true;
    }
    else {
      return false;
    }
  }

  /*
  @purpose: To recursively set the pool to draw assessors from in the assessment
  stops when the pool of potential assessors is 20 times the size of the assessment2
  @param: uint seed = the seed number for random number generation
  @param: address _concept = the concept being called from
  @param: uint num = the number of assessors to be called
  */
  function setAssessorPool(uint seed, address _concept, uint num) onlyConceptAssessment() {
    uint numCalled = 0;
    for (uint k=0; k < num; k++) {
      if (assessorPoolLength == size*20) { return; } //
      address randomUser = Concept(_concept).getRandomMember(seed + k);
      if (addAssessorToPool(randomUser)) {
        numCalled++;
      }
    }
    uint remaining = num - numCalled;
    if (remaining > 0) {
      for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
        setAssessorPool(seed + assessorPoolLength, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
      }
    }
    assessmentStage = State.Called;
  }

  /*
    @purpose: To set the pool of assessors when there are so few users in the system that
    assembling them at random is not meaningful. This will use all members of mew instead
  */
  function setAssessorPoolFromMew() onlyConcept() {
    Concept mew = Concept(ConceptRegistry(conceptRegistry).mewAddress()); //TODO: can this be more elegant and/or in one line?
      for (uint i=0; i<mew.getMemberLength(); i++) {
        addAssessorToPool(mew.members(i));
      }
      size = mew.getMemberLength();
      assessmentStage = State.Called;
  }

  //@purpose: called by an assessor to confirm and stake
  function confirmAssessor() onlyInStage(State.Called){
    //Check if the time to confirm has not passed, that the assessor was actually called, assessors are still needed, and that the assessor has enough of a balance to pay the stake
    if (assessorState[msg.sender] == State.Called &&
        assessors.length < size &&
        Concept(concept).subtractBalance(msg.sender, cost)
       )
      {
      assessors.push(msg.sender); //Adds the user that called this function to the array of confirmed assessors
      assessorState[msg.sender] = State.Confirmed; //Sets the assessor's state to confirmed
      stake[msg.sender] = cost; //Sets the current stake value of the assessor to the cost of the assessment
      UserRegistry(userRegistry).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
    }
    if (assessors.length == size) { //If enough assessors have been called
      notifyAssessors(uint(State.Confirmed), 4);
      UserRegistry(userRegistry).notification(assessee, 4);
      assessmentStage = State.Confirmed;
    }
  }

  function setData(string _data) onlyAssessorAssessee() {
    data[msg.sender].push(_data); //Adds the data to an array corresponding to the user that uploaded it
    DataSet(msg.sender, data[msg.sender].length - 1); // fire event
  }

  //@purpose: called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
  function commit(bytes32 hash) onlyInStage(State.Confirmed){
    //If more than half the assessors have committed their score hash:
    if(done > size/2) {
        burnStakes();
    }
    //If all the assessors have either committed or had their entire stake burned
    if(done == size) {
      notifyAssessors(uint(State.Committed), 5);
      startTime = block.number; //Resets the startTime
      done = 0; //Resets the done counter
      assessmentStage = State.Committed;
    }
    //If the caller is confirmed as an assessor
    if(assessorState[msg.sender] == State.Confirmed)
    {
      commits[msg.sender] = hash; //Maps the score hash to the assessor
      assessorState[msg.sender] = State.Committed; //Sets the assessor's state to Committed
      done++; //Increases done by 1 to help progress to the next assessment stage.

      if(done <= size/2) //If less than or half the assessors have committed
      {
        startTime = block.number; //Sets the startTime to the current block
      }
    }
  }

  function burnStakes() internal {
      for(uint i = 0; i < assessors.length; i++) {
          if(assessorState[assessors[i]] == State.Confirmed) { //if the assessor has not committed  a score
              uint r = 38; //Inverse burn rate
              stake[assessors[i]] = cost*2**(-(now-startTime)/r) - 1; //burns stake as a function of how much time has passed since half of the assessors commited
          }
          if(stake[assessors[i]] == 0) { //If an assessor's stake is entirely burned
              assessorState[assessors[i]] = State.Burned;
              done++; //Increases done by 1 to help progress to the next assessment stage
          }
      }
  }

  //@purpose: called by assessors to reveal their own commits or others
  function reveal(int8 score, bytes16 salt, address assessor) onlyInStage(State.Committed) {
    if(block.number - startTime <= 10) { //If less than 10 blocks have passed since the socre commiting stage ended
      bytes32 hash = sha3(score,salt);
      if(commits[assessor] == hash) {
        if(msg.sender == assessor) {
          scores[msg.sender] = score;
          assessorState[assessor] = State.Done;
          done++; //done increases by 1 to help progress to the next assessment stage
        }
        else { //If it was submitted by another user than the given assessor
          Concept(concept).addBalance(msg.sender, stake[assessor]); // give the stake to the caller
          stake[assessor] = 0;
          assessorState[assessor] = State.Burned;
          done++; //done increases by 1 to help progress to the next assessment stage
        }
      }
      else if(msg.sender == assessor) { //If the stake was wrong and was provided by the assessor in the parameter
        stake[assessor] = 0; //The assessor's stake is completely burned
        assessorState[assessor] = State.Burned; //The assessor's state is set to Burned
        done++; //done increases by 1 to help progress to the next assessment stage

    }
    else { //If more than 10 blocks have passed
      for(uint i = 0; i < assessors.length; i++) { //Loops through all the assessors
        if(assessorState[assessors[i]] == State.Committed) { //If the assessor has not revealed their score
          stake[assessors[i]] = 0; //The assessor's stake is completely burned
          assessorState[assessors[i]] = State.Burned;
          done++; //done increases by 1 to help progress to the next assessment stage
        }
      }
    }
    if(done == size) { //If all the assessors have revealed their scored or burned their stakes
      assessmentStage = State.Done;
      calculateResult(); //The final result is calculated
    }
  }

  function calculateResult() private {
   function calculateResult() onlyInStage(State.Done) private {
    for(uint j = 0; j < size; j++) { //Loops through the assessors
      if(assessorState[assessors[i]] == State.Done) {
        finalAssessors.push(assessors[i]); //Adds all the assessors that completed the assessment process to an array
      }
    }
    int[] memory score = new int[] (finalAssessors.length); //Initializes an array to store scores that is the same length as the number of finalAssessors
    uint largestClusterIndex = 0; //store the index of the largest cluster
    int averageScore;
    for(uint i = 0; i < finalAssessors.length; i++) {
      score[i] = scores[finalAssessors[i]]; //Puts all the scores in the score array
    }
    int meanAbsoluteDeviation = Math.calculateMAD(score,int(finalAssessors.length));
    for(uint l = 0; l < score.length; l++) { //Find the largest cluster of scores
      for(uint m = 0; m < score.length; m++)
      {
        if(score[l] - score[m] <= meanAbsoluteDeviation) {
          clusters[l].push(score[m]);
        }
      }
      if(clusters[l].length > clusters[largestClusterIndex].length) {
        largestClusterIndex = l;
      }
    }
    for(uint o = 0; o < clusters[largestClusterIndex].length; o++) { //Records which assessors submitted a score that was in the largest cluster
      averageScore += clusters[largestClusterIndex][o];
      inRewardCluster[clusters[largestClusterIndex][o]] = true;
    }
    averageScore /= int(clusters[largestClusterIndex].length); //Calculates the average score from the largest cluster
    finalScore = averageScore; //Sets the final score to the average score
    payout(); //Initializes the payout sequence
  }

  function payout() onlyInStage(State.Done) private {
    for(uint i = 0; i < size; i++) { //Loops through all the confirmed assessors
      int score = scores[assessors[i]]; //Gets the score of an assessor
      int scoreDistance = ((score - finalScore)*100)/finalScore; //Calculates the percent difference between the final score the assessors score
      if(scoreDistance < 0) { //Makes negative scoreDistances positive
        scoreDistance *= -1;
      }
      uint payoutValue = 1; //Initializes the payoutValue for the assessor
      if(inRewardCluster[score] == true) { //If the assessor's score was in the largest cluster
        uint q = 1; //Inflation rate factor, WE NEED TO FIGURE THIS OUT AT SOME POINT
        payoutValue = q*cost*((100 - uint(scoreDistance))/100); //The assessor's payout will be some constant times a propotion of their original stake determined by how close to the final score they were
        Concept(concept).addBalance(assessors[i], payoutValue); //Pays the user
      }
      if(inRewardCluster[score] == false) { //If the assessor's score wasn't in the largest cluster
        payoutValue = stake[assessors[i]]*((200 - uint(scoreDistance))/200); //The assessor's payout will be a propotion of their remaining stake determined by their distance from the final score
        Concept(concept).addBalance(assessors[i], payoutValue); //Pays the user
      }
    Concept(concept).finishAssessment(finalScore, assessee, address(this)); //Sends assessment info to the concept so that it can update its records
    UserRegistry(userRegistry).notification(assessors[i], 6); //You Have Received Payment For Your Assessment
    }
  }
}

pragma solidity ^0.4.0;

import "./Math.sol";
import "./Concept.sol";
import "./ConceptRegistry.sol";
import "./UserRegistry.sol";

contract Assessment {
  address assessee; //The address of the user being assessed
  address[] assessors;
  address[] finalAssessors; //The assessors who actually give an assessment
  mapping (address => State) assessorState;
  mapping(uint => int[]) clusters; //Initializes mapping to store clusters of scores
  State assessmentStage;
  enum State {//The state of the assessors
    None,
    Called, //Been called as a potential assessor
    Confirmed, //Confirmed as assessing
    Committed, //Committed a score
    Done, //Completed the assessment processs
    Burned //Entire stake has been burned
  }

  uint public assessorPoolLength; //how many users are potential assessors (also changed to public)
  address concept;
  address userRegistry;
  address conceptRegistry;
  uint public startTime;
  uint public size;
  uint cost;
  mapping(address => string[]) public data; //IPFS hashes of data that can be passed between the assessors and the assessee for the assessment to occur
  mapping(address => bytes32) commits;
  mapping(address => uint) stake;
  uint done = 0;
  mapping(address => int8) scores; //could save gas by making this an int8
  mapping(int => bool) inRewardCluster;
  int finalScore;
  event DataSet(address _dataSetter, uint _index);

  modifier onlyAssessorAssessee() {
    if(msg.sender != assessee && uint(assessorState[msg.sender]) == 0) {
      throw;
    }
    _;
  }

  modifier onlyConceptAssessment() {
    if(msg.sender != address(this) && msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyConcept() {
    if(msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyInStage(State _stage) {
      if(assessmentStage != _stage){
          throw;
      }
      _;
  }

  function Assessment(address _assessee, address _userRegistry, address _conceptRegistry, uint _size, uint _cost) {
    assessee = _assessee;
    concept = msg.sender;
    userRegistry = _userRegistry;
    conceptRegistry = _conceptRegistry;
    startTime = block.number;
    size = _size;
    cost = _cost;
    UserRegistry(userRegistry).notification(assessee, 0); // assesse has started an assessment
    assessorPoolLength = 0;
  }

  function cancelAssessment() onlyInStage(State.Called) {
    if(msg.sender != assessee) throw;
    Concept(concept).addBalance(assessee, cost*size);
    UserRegistry(userRegistry).notification(assessee, 3); //Assessment Cancled and you have been refunded
    for(uint i = 0; i < assessors.length; i++) {
      Concept(concept).addBalance(assessors[i], cost);
      UserRegistry(userRegistry).notification(assessors[i], 3); //Assessment Cancled and you have been refunded
    }
    suicide(conceptRegistry);
  }

  //@purpose: adds a user to the pool eligible to accept an assessment
  function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
    if(assessorState[assessor] == State.None) {
      UserRegistry(userRegistry).notification(assessor, 1); //Called As A Potential Assessor
      assessorState[assessor] = State.Called; //Sets the state of the assessor as called
      assessorPoolLength++;
      return true;
    }
    else {
      return false;
    }
  }

  /*
  @purpose: To recursively set the pool to draw assessors from in the assessment
  stops when the pool of potential assessors is 20 times the size of the assessment2
  @param: uint seed = the seed number for random number generation
  @param: address _concept = the concept being called from
  @param: uint num = the number of assessors to be called
  */
  function setAssessorPool(uint seed, address _concept, uint num) onlyConceptAssessment() {
    uint numCalled = 0;
    for (uint k=0; k < num; k++) {
      if (assessorPoolLength == size*20) { return; } //
      address randomUser = Concept(_concept).getRandomMember(seed + k);
      if (addAssessorToPool(randomUser)) {
        numCalled++;
      }
    }
    uint remaining = num - numCalled;
    if (remaining > 0) {
      for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
        setAssessorPool(seed + assessorPoolLength, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
      }
    }
    assessmentStage = State.Called;
  }

  /*
    @purpose: To set the pool of assessors when there are so few users in the system that
    assembling them at random is not meaningful. This will use all members of mew instead
  */
  function setAssessorPoolFromMew() onlyConcept() {
    Concept mew = Concept(ConceptRegistry(conceptRegistry).mewAddress()); //TODO: can this be more elegant and/or in one line?
      for (uint i=0; i<mew.getMemberLength(); i++) {
        addAssessorToPool(mew.members(i));
      }
      size = mew.getMemberLength();
      assessmentStage = State.Called;
  }

  //@purpose: called by an assessor to confirm and stake
  function confirmAssessor() onlyInStage(State.Called){
    //Check if the time to confirm has not passed, that the assessor was actually called, assessors are still needed, and that the assessor has enough of a balance to pay the stake
    if (assessorState[msg.sender] == State.Called &&
        assessors.length < size &&
        Concept(concept).subtractBalance(msg.sender, cost)
       )
      {
      assessors.push(msg.sender); //Adds the user that called this function to the array of confirmed assessors
      assessorState[msg.sender] = State.Confirmed; //Sets the assessor's state to confirmed
      stake[msg.sender] = cost; //Sets the current stake value of the assessor to the cost of the assessment
      UserRegistry(userRegistry).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
    }
  if (assessors.length == size) { //If enough assessors have been called
      notifyAssessors(uint(State.Confirmed), 4);
      UserRegistry(userRegistry).notification(assessee, 4);
  } else if (block.number - startTime > 15 && assessors.length < size){
      cancelAssessment(); //Cancels the assessment if not enough assessors confirm within 15 blocks
  }
  }

  function notifyAssessors(uint _state, uint _topic) private {
      for(uint i=0; i < assessors.length; i++) {
          if(uint(assessorState[assessors[i]]) == _state) {
              UserRegistry(userRegistry).notification(assessors[i], _topic);
          }
      }
      assessmentStage = State.Confirmed;
    }
  }

  function setData(string _data) onlyAssessorAssessee() {
    data[msg.sender].push(_data); //Adds the data to an array corresponding to the user that uploaded it
    DataSet(msg.sender, data[msg.sender].length - 1); // fire event
  }

  //@purpose: called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
  function commit(bytes32 hash) onlyInStage(State.Confirmed){
    //If more than half the assessors have committed their score hash:
    if(done > size/2) {
        burnStakes();
    }
    //If all the assessors have either committed or had their entire stake burned
    if(done == size) {
      notifyAssessors(uint(State.Committed), 5);
      startTime = block.number; //Resets the startTime
      done = 0; //Resets the done counter
      assessmentStage = State.Committed;
    }
    //If the caller is confirmed as an assessor
    if(assessorState[msg.sender] == State.Confirmed)
    {
      commits[msg.sender] = hash; //Maps the score hash to the assessor
      assessorState[msg.sender] = State.Committed; //Sets the assessor's state to Committed
      done++; //Increases done by 1 to help progress to the next assessment stage.

      if(done <= size/2) //If less than or half the assessors have committed
      {
        startTime = block.number; //Sets the startTime to the current block
      }
    }
  }

  function burnStakes() internal {
      for(uint i = 0; i < assessors.length; i++) {
          if(assessorState[assessors[i]] == State.Confirmed) { //if the assessor has not committed  a score
              uint r = 38; //Inverse burn rate
              stake[assessors[i]] = cost*2**(-(now-startTime)/r) - 1; //burns stake as a function of how much time has passed since half of the assessors commited
          }
          if(stake[assessors[i]] == 0) { //If an assessor's stake is entirely burned
              assessorState[assessors[i]] = State.Burned;
              done++; //Increases done by 1 to help progress to the next assessment stage
          }
      }
  }

  //@purpose: called by assessors to reveal their own commits or others
  function reveal(int8 score, bytes16 salt, address assessor) onlyInStage(State.Committed) {
    if(block.number - startTime <= 10) { //If less than 10 blocks have passed since the socre commiting stage ended
      bytes32 hash = sha3(score,salt);
      if(commits[assessor] == hash) {
        if(msg.sender == assessor) {
          scores[msg.sender] = score;
          assessorState[assessor] = State.Done;
          done++; //done increases by 1 to help progress to the next assessment stage
        }
        else { //If it was submitted by another user than the given assessor
          Concept(concept).addBalance(msg.sender, stake[assessor]); // give the stake to the caller
          stake[assessor] = 0;
          assessorState[assessor] = State.Burned;
          done++; //done increases by 1 to help progress to the next assessment stage
        }
      }
      else if(msg.sender == assessor) { //If the stake was wrong and was provided by the assessor in the parameter
        stake[assessor] = 0; //The assessor's stake is completely burned
        assessorState[assessor] = State.Burned; //The assessor's state is set to Burned
        done++; //done increases by 1 to help progress to the next assessment stage

    }
    else { //If more than 10 blocks have passed
      for(uint i = 0; i < assessors.length; i++) { //Loops through all the assessors
        if(assessorState[assessors[i]] == State.Committed) { //If the assessor has not revealed their score
          stake[assessors[i]] = 0; //The assessor's stake is completely burned
          assessorState[assessors[i]] = State.Burned;
          done++; //done increases by 1 to help progress to the next assessment stage
        }
      }
    }
    if(done == size) { //If all the assessors have revealed their scored or burned their stakes
      assessmentStage = State.Done;
      calculateResult(); //The final result is calculated
    }
  }

  function calculateResult() private {
   function calculateResult() onlyInStage(State.Done) private {
    for(uint j = 0; j < size; j++) { //Loops through the assessors
      if(assessorState[assessors[i]] == State.Done) {
        finalAssessors.push(assessors[i]); //Adds all the assessors that completed the assessment process to an array
      }
    }
    int[] memory score = new int[] (finalAssessors.length); //Initializes an array to store scores that is the same length as the number of finalAssessors
    uint largestClusterIndex = 0; //store the index of the largest cluster
    int averageScore;
    for(uint i = 0; i < finalAssessors.length; i++) {
      score[i] = scores[finalAssessors[i]]; //Puts all the scores in the score array
    }
    int meanAbsoluteDeviation = Math.calculateMAD(score,int(finalAssessors.length));
    for(uint l = 0; l < score.length; l++) { //Find the largest cluster of scores
      for(uint m = 0; m < score.length; m++)
      {
        if(score[l] - score[m] <= meanAbsoluteDeviation) {
          clusters[l].push(score[m]);
        }
      }
      if(clusters[l].length > clusters[largestClusterIndex].length) {
        largestClusterIndex = l;
      }
    }
    for(uint o = 0; o < clusters[largestClusterIndex].length; o++) { //Records which assessors submitted a score that was in the largest cluster
      averageScore += clusters[largestClusterIndex][o];
      inRewardCluster[clusters[largestClusterIndex][o]] = true;
    }
    averageScore /= int(clusters[largestClusterIndex].length); //Calculates the average score from the largest cluster
    finalScore = averageScore; //Sets the final score to the average score
    payout(); //Initializes the payout sequence
  }

  function payout() onlyInStage(State.Done) private {
    for(uint i = 0; i < size; i++) { //Loops through all the confirmed assessors
      int score = scores[assessors[i]]; //Gets the score of an assessor
      int scoreDistance = ((score - finalScore)*100)/finalScore; //Calculates the percent difference between the final score the assessors score
      if(scoreDistance < 0) { //Makes negative scoreDistances positive
        scoreDistance *= -1;
      }
      uint payoutValue = 1; //Initializes the payoutValue for the assessor
      if(inRewardCluster[score] == true) { //If the assessor's score was in the largest cluster
        uint q = 1; //Inflation rate factor, WE NEED TO FIGURE THIS OUT AT SOME POINT
        payoutValue = q*cost*((100 - uint(scoreDistance))/100); //The assessor's payout will be some constant times a propotion of their original stake determined by how close to the final score they were
        Concept(concept).addBalance(assessors[i], payoutValue); //Pays the user
      }
      if(inRewardCluster[score] == false) { //If the assessor's score wasn't in the largest cluster
        payoutValue = stake[assessors[i]]*((200 - uint(scoreDistance))/200); //The assessor's payout will be a propotion of their remaining stake determined by their distance from the final score
        Concept(concept).addBalance(assessors[i], payoutValue); //Pays the user
      }
    Concept(concept).finishAssessment(finalScore, assessee, address(this)); //Sends assessment info to the concept so that it can update its records
    UserRegistry(userRegistry).notification(assessors[i], 6); //You Have Received Payment For Your Assessment
    }
  }
}


Rename modifier name

Rename modifier name

contract Conference {
	string public name;
	uint256 public balance;
	uint256 public deposit;
	uint public limitOfParticipants;
	uint public registered;
	uint public attended;
	address public owner;
	bool public ended;
	mapping (address => Participant) public participants;
	mapping (uint => address) public participantsIndex;
	bool paid;
	uint256 _payout;

	struct Participant {
		string participantName;
		address addr;
		bool attended;
		int payout;
	}

	event Register(string participantName, address addr, uint256 balance, uint256 value);
	event Attend(address addr, uint256 balance);
	event Payback(address addr, uint256 _payout, uint256 balance, bool paid);
	event Cancel(address addr, uint256 balance);

	function Conference() {
		name = 'CodeUp';
		deposit = 1000000000000000000;		// 1 ETH = 10**18 wai
		balance = 0;
		registered = 0;
		attended = 0;
		limitOfParticipants = 10;
		owner = msg.sender;
		ended = false;
	}

	modifier sentDepositOrReturn {
		if (msg.value == deposit) {
			_
		}else{
			msg.sender.send(msg.value);
		}
	}
	modifier onlyActiveOrReturn {
		if (ended == false) {
			_
		}else{
			msg.sender.send(msg.value);
		}
	}

	modifier withinLimitOrReturn {
		if (registered < limitOfParticipants ) {
			_
		}else{
			msg.sender.send(msg.value);
		}
	}

	function register(string _participant) sentDepositOrReturn withinLimitOrReturn onlyActiveOrReturn{
		Register(_participant, msg.sender, msg.sender.balance, msg.value);
		if (isRegistered(msg.sender)) throw;
		registered++;
		participantsIndex[registered] = msg.sender;
		participants[msg.sender] = Participant(_participant, msg.sender, false, 0);
		balance = balance + (deposit * 1);
	}

	function setLimitOfParticipants(uint _limitOfParticipants) {
		limitOfParticipants = _limitOfParticipants;
	}

	modifier onlyByOwner {
		if (msg.sender == owner) _
	}

	function attend(address _addr) onlyByOwner{
		if (isRegistered(_addr) != true) throw;
		if (isAttended(_addr)) throw;
		Attend(_addr, msg.sender.balance);
		participants[_addr].attended = true;
		attended++;
	}

	function isRegistered(address _addr) returns (bool){
		return participants[_addr].addr != 0x0;
	}

	function isAttended(address _addr) returns (bool){
		return isRegistered(_addr) && participants[_addr].attended;
	}

	function payout() returns(uint256){
		return balance / uint(attended);
	}

	function payback() onlyByOwner{
		for(uint i=1;i<=registered;i++)
		{
			if(participants[participantsIndex[i]].attended){
				Payback(participantsIndex[i], payout(), participantsIndex[i].balance,  true);
				participants[participantsIndex[i]].payout = int(payout() - deposit);
				participantsIndex[i].send(payout());
			}else{
				participants[participantsIndex[i]].payout = int(deposit - (deposit * 2));
				Payback(participantsIndex[i], payout(), participantsIndex[i].balance, false);
			}
		}
		balance = 0;
		ended = true;
	}

	function cancel() onlyByOwner{
		Cancel(owner, balance);
		for(uint i=1;i<=registered;i++)
		{
			if(balance > 0){
				participantsIndex[i].send(deposit);
			}
			delete participants[participantsIndex[i]];
			delete participantsIndex[i];
		}
		balance = 0;
		registered = 0;
		attended = 0;
		ended = true;
	}
}

contract Conference {
	string public name;
	uint256 public balance;
	uint256 public deposit;
	uint public limitOfParticipants;
	uint public registered;
	uint public attended;
	address public owner;
	bool public ended;
	mapping (address => Participant) public participants;
	mapping (uint => address) public participantsIndex;
	bool paid;
	uint256 _payout;

	struct Participant {
		string participantName;
		address addr;
		bool attended;
		int payout;
	}

	event Register(string participantName, address addr, uint256 balance, uint256 value);
	event Attend(address addr, uint256 balance);
	event Payback(address addr, uint256 _payout, uint256 balance, bool paid);
	event Cancel(address addr, uint256 balance);

	function Conference() {
		name = 'CodeUp';
		deposit = 1000000000000000000;		// 1 ETH = 10**18 wai
		balance = 0;
		registered = 0;
		attended = 0;
		limitOfParticipants = 10;
		owner = msg.sender;
		ended = false;
	}

	modifier sentDeposit {
		if (msg.value == deposit) {
			_
		}else{
			msg.sender.send(msg.value);
		}
	}
	modifier onlyActive {
		if (ended == false) {
			_
		}else{
			msg.sender.send(msg.value);
		}
	}

	modifier withinLimit {
		if (registered < limitOfParticipants ) {
			_
		}else{
			msg.sender.send(msg.value);
		}
	}

	function register(string _participant) sentDeposit withinLimit onlyActive{
		Register(_participant, msg.sender, msg.sender.balance, msg.value);
		if (isRegistered(msg.sender)) throw;
		registered++;
		participantsIndex[registered] = msg.sender;
		participants[msg.sender] = Participant(_participant, msg.sender, false, 0);
		balance = balance + (deposit * 1);
	}

	function setLimitOfParticipants(uint _limitOfParticipants) {
		limitOfParticipants = _limitOfParticipants;
	}

	modifier onlyByOwner {
		if (msg.sender == owner) _
	}

	function attend(address _addr) onlyByOwner{
		if (isRegistered(_addr) != true) throw;
		if (isAttended(_addr)) throw;
		Attend(_addr, msg.sender.balance);
		participants[_addr].attended = true;
		attended++;
	}

	function isRegistered(address _addr) returns (bool){
		return participants[_addr].addr != 0x0;
	}

	function isAttended(address _addr) returns (bool){
		return isRegistered(_addr) && participants[_addr].attended;
	}

	function payout() returns(uint256){
		return balance / uint(attended);
	}

	function payback() onlyByOwner{
		for(uint i=1;i<=registered;i++)
		{
			if(participants[participantsIndex[i]].attended){
				Payback(participantsIndex[i], payout(), participantsIndex[i].balance,  true);
				participants[participantsIndex[i]].payout = int(payout() - deposit);
				participantsIndex[i].send(payout());
			}else{
				participants[participantsIndex[i]].payout = int(deposit - (deposit * 2));
				Payback(participantsIndex[i], payout(), participantsIndex[i].balance, false);
			}
		}
		balance = 0;
		ended = true;
	}

	function cancel() onlyByOwner{
		Cancel(owner, balance);
		for(uint i=1;i<=registered;i++)
		{
			if(balance > 0){
				participantsIndex[i].send(deposit);
			}
			delete participants[participantsIndex[i]];
			delete participantsIndex[i];
		}
		balance = 0;
		registered = 0;
		attended = 0;
		ended = true;
	}
}


Made SampleCrowdsale a bit clearer. (#1448)

pragma solidity ^0.4.24;

import "../crowdsale/validation/CappedCrowdsale.sol";
import "../crowdsale/distribution/RefundableCrowdsale.sol";
import "../crowdsale/emission/MintedCrowdsale.sol";
import "../token/ERC20/ERC20Mintable.sol";
import "../token/ERC20/ERC20Detailed.sol";

/**
 * @title SampleCrowdsaleToken
 * @dev Very simple ERC20 Token that can be minted.
 * It is meant to be used in a crowdsale contract.
 */
contract SampleCrowdsaleToken is ERC20Mintable, ERC20Detailed {
  constructor() public ERC20Detailed("Sample Crowdsale Token", "SCT", 18) {}
}

/**
 * @title SampleCrowdsale
 * @dev This is an example of a fully fledged crowdsale.
 * The way to add new features to a base crowdsale is by multiple inheritance.
 * In this example we are providing following extensions:
 * CappedCrowdsale - sets a max boundary for raised funds
 * RefundableCrowdsale - set a min goal to be reached and returns funds if it's not met
 * MintedCrowdsale - assumes the token can be minted by the crowdsale, which does so
 * when receiving purchases.
 *
 * After adding multiple features it's good practice to run integration tests
 * to ensure that subcontracts works together as intended.
 */
// XXX There doesn't seem to be a way to split this line that keeps solium
// happy. See:
// https://github.com/duaraghav8/Solium/issues/205
// --elopio - 2018-05-10
// solium-disable-next-line max-len
contract SampleCrowdsale is CappedCrowdsale, RefundableCrowdsale, MintedCrowdsale {

  constructor(
    uint256 openingTime,
    uint256 closingTime,
    uint256 rate,
    address wallet,
    uint256 cap,
    ERC20Mintable token,
    uint256 goal
  )
    public
    Crowdsale(rate, wallet, token)
    CappedCrowdsale(cap)
    TimedCrowdsale(openingTime, closingTime)
    RefundableCrowdsale(goal)
  {
    //As goal needs to be met for a successful crowdsale
    //the value needs to less or equal than a cap which is limit for accepted funds
    require(goal <= cap);
  }
}

pragma solidity ^0.4.24;

import "../crowdsale/validation/CappedCrowdsale.sol";
import "../crowdsale/distribution/RefundableCrowdsale.sol";
import "../crowdsale/emission/MintedCrowdsale.sol";
import "../token/ERC20/ERC20Mintable.sol";

/**
 * @title SampleCrowdsaleToken
 * @dev Very simple ERC20 Token that can be minted.
 * It is meant to be used in a crowdsale contract.
 */
contract SampleCrowdsaleToken is ERC20Mintable {

  string public constant name = "Sample Crowdsale Token";
  string public constant symbol = "SCT";
  uint8 public constant decimals = 18;
}

/**
 * @title SampleCrowdsale
 * @dev This is an example of a fully fledged crowdsale.
 * The way to add new features to a base crowdsale is by multiple inheritance.
 * In this example we are providing following extensions:
 * CappedCrowdsale - sets a max boundary for raised funds
 * RefundableCrowdsale - set a min goal to be reached and returns funds if it's not met
 *
 * After adding multiple features it's good practice to run integration tests
 * to ensure that subcontracts works together as intended.
 */
// XXX There doesn't seem to be a way to split this line that keeps solium
// happy. See:
// https://github.com/duaraghav8/Solium/issues/205
// --elopio - 2018-05-10
// solium-disable-next-line max-len
contract SampleCrowdsale is CappedCrowdsale, RefundableCrowdsale, MintedCrowdsale {

  constructor(
    uint256 openingTime,
    uint256 closingTime,
    uint256 rate,
    address wallet,
    uint256 cap,
    ERC20Mintable token,
    uint256 goal
  )
    public
    Crowdsale(rate, wallet, token)
    CappedCrowdsale(cap)
    TimedCrowdsale(openingTime, closingTime)
    RefundableCrowdsale(goal)
  {
    //As goal needs to be met for a successful crowdsale
    //the value needs to less or equal than a cap which is limit for accepted funds
    require(goal <= cap);
  }
}


contract: remove fallback - throws by default in solidity ^0.4.0

//! Fundraiser contract. Just records who sent what.
//! By Parity Technologies, 2017.
//! Released under the Apache Licence 2.
//! Modified by the Interchain Foundation.

pragma solidity ^0.4.7;

/// Will accept Ether "contributions" and record each both as a log and in a
/// queryable record.
contract Fundraiser {

    // How much is enough?
    uint public constant dust = 1 finney; // XXX: change for production

    // Special addresses:
    //  administrator can halt/unhalt/kill/adjustRate;
    //  treasury receives all the funds
    address public admin;
    address public treasury;

    // Begin and end block for the fundraising period
    uint public beginBlock;
    uint public endBlock;

    // Number of atoms per ether.
    // Correspondingly, number of 10^(-18)atoms per wei
    // TODO: change units so it can handle values < 1 (ie. if eth price tanks!)
    uint public atomRate;

    // Are contributions abnormally halted?
    bool public isHalted = false;

    // The record mapping maps cosmos addresses to the amount of atoms - the sum of
    // products of donation amounts by the atomRate when the donation was received.
    // The returnAddresses mapping maps cosmos addresses to their ethereum return addresses.
    // NOTE: We could use a struct instead but would currently
    // save only one SSLOAD/SSTORE and at the expense
    // of relying on solidity type packing :o !
    mapping (address => uint) public record;
    mapping (address => address) public returnAddresses;

    // The total amount of ether raised
    uint public totalEther = 0;
    // The total amount of atoms suggested for allocation
    uint public totalAtom = 0;

    /// Constructor. `_admin` has the ability to pause the
    /// contribution period and, eventually, kill this contract. `_treasury`
    /// receives all funds. `_beginBlock` and `_endBlock` define the begin and
    /// end of the period. `_atomRate` is the ratio of atoms to ether.
    function Fundraiser(address _admin, address _treasury, uint _beginBlock, uint _endBlock, uint _atomRate) {
        admin = _admin;
        treasury = _treasury;
        beginBlock = _beginBlock;
        endBlock = _endBlock;
	atomRate = _atomRate;
    }

    // Can only be called by _admin.
    modifier only_admin { if (msg.sender != admin) throw; _; }
    // Can only be called by prior to the period.
    modifier only_before_period { if (block.number >= beginBlock) throw; _; }
    // Can only be called during the period when not halted.
    modifier only_during_period { if (block.number < beginBlock || block.number >= endBlock || isHalted) throw; _; }
    // Can only be called during the period when halted.
    modifier only_during_halted_period { if (block.number < beginBlock || block.number >= endBlock || !isHalted) throw; _; }
    // Can only be called after the period.
    modifier only_after_period { if (block.number < endBlock) throw; _; }
    // The value of the message must be sufficiently large to not be considered dust.
    modifier is_not_dust { if (msg.value < dust) throw; _; }

    /// Some contribution `amount` received from `recipient` at rate of `currentRate`.
    event Received(address indexed recipient, uint amount, uint currentRate);
    /// Period halted abnormally.
    event Halted();
    /// Period restarted after abnormal halt.
    event Unhalted();

    /// Receive a contribution for a donor cosmos address.
    /// Also store returnAddress just-in-case.
    function donate(address _donor, address _returnAddress, bytes32 checksum) payable only_during_period is_not_dust {
	// checksum is the sha3 of the xor of the bytes32 versions of the cosmos address and the return address
	if (!(sha3(bytes32(_donor)^bytes32(_returnAddress)) == checksum)) throw;

	// forward the funds to the treasurer
        if (!treasury.send(msg.value)) throw;

	// calculate the number of atoms at the current rate
	var atoms = msg.value * atomRate;

	// update the donor details
        record[_donor] += atoms;

	// only set return address on first donation
	if ( returnAddresses[_donor] == address(0x0) ) {
        	returnAddresses[_donor] = _returnAddress;
	}

	// update the totals
        totalEther += msg.value;
	totalAtom += atoms;

        Received(_donor, msg.value, atomRate);
    }

    /// Adjust the atomRate
    /// NOTE: Should we allow rate to adjust before the period starts?
    function adjustRate(uint newRate) only_admin only_during_period {
	atomRate = newRate;
    }

    /// Halt the contribution period. Any attempt at contributing will fail.
    function halt() only_admin only_during_period {
        isHalted = true;
        Halted();
    }

    /// Unhalt the contribution period.
    function unhalt() only_admin only_during_halted_period {
        isHalted = false;
        Unhalted();
    }

    /// Kill this contract.
    function kill() only_admin only_after_period {
        suicide(treasury);
    }
}

//! Fundraiser contract. Just records who sent what.
//! By Parity Technologies, 2017.
//! Released under the Apache Licence 2.
//! Modified by the Interchain Foundation.

pragma solidity ^0.4.7;

/// Will accept Ether "contributions" and record each both as a log and in a
/// queryable record.
contract Fundraiser {

    // How much is enough?
    uint public constant dust = 1 finney; // XXX: change for production

    // Special addresses:
    //  administrator can halt/unhalt/kill/adjustRate;
    //  treasury receives all the funds
    address public admin;
    address public treasury;

    // Begin and end block for the fundraising period
    uint public beginBlock;
    uint public endBlock;

    // Number of atoms per ether.
    // Correspondingly, number of 10^(-18)atoms per wei
    // TODO: change units so it can handle values < 1 (ie. if eth price tanks!)
    uint public atomRate;

    // Are contributions abnormally halted?
    bool public isHalted = false;

    // The record mapping maps cosmos addresses to the amount of atoms - the sum of
    // products of donation amounts by the atomRate when the donation was received.
    // The returnAddresses mapping maps cosmos addresses to their ethereum return addresses.
    // NOTE: We could use a struct instead but would currently
    // save only one SSLOAD/SSTORE and at the expense
    // of relying on solidity type packing :o !
    mapping (address => uint) public record;
    mapping (address => address) public returnAddresses;

    // The total amount of ether raised
    uint public totalEther = 0;
    // The total amount of atoms suggested for allocation
    uint public totalAtom = 0;

    /// Constructor. `_admin` has the ability to pause the
    /// contribution period and, eventually, kill this contract. `_treasury`
    /// receives all funds. `_beginBlock` and `_endBlock` define the begin and
    /// end of the period. `_atomRate` is the ratio of atoms to ether.
    function Fundraiser(address _admin, address _treasury, uint _beginBlock, uint _endBlock, uint _atomRate) {
        admin = _admin;
        treasury = _treasury;
        beginBlock = _beginBlock;
        endBlock = _endBlock;
	atomRate = _atomRate;
    }

    // Can only be called by _admin.
    modifier only_admin { if (msg.sender != admin) throw; _; }
    // Can only be called by prior to the period.
    modifier only_before_period { if (block.number >= beginBlock) throw; _; }
    // Can only be called during the period when not halted.
    modifier only_during_period { if (block.number < beginBlock || block.number >= endBlock || isHalted) throw; _; }
    // Can only be called during the period when halted.
    modifier only_during_halted_period { if (block.number < beginBlock || block.number >= endBlock || !isHalted) throw; _; }
    // Can only be called after the period.
    modifier only_after_period { if (block.number < endBlock) throw; _; }
    // The value of the message must be sufficiently large to not be considered dust.
    modifier is_not_dust { if (msg.value < dust) throw; _; }

    /// Some contribution `amount` received from `recipient` at rate of `currentRate`.
    event Received(address indexed recipient, uint amount, uint currentRate);
    /// Period halted abnormally.
    event Halted();
    /// Period restarted after abnormal halt.
    event Unhalted();

    /// Fallback function throws. Successful donation requires arguments
    function() {
	throw;
    }

    /// Receive a contribution for a donor cosmos address.
    /// Also store returnAddress just-in-case.
    function donate(address _donor, address _returnAddress, bytes32 checksum) payable only_during_period is_not_dust {
	// checksum is the sha3 of the xor of the bytes32 versions of the cosmos address and the return address
	if (!(sha3(bytes32(_donor)^bytes32(_returnAddress)) == checksum)) throw;

	// forward the funds to the treasurer
        if (!treasury.send(msg.value)) throw;

	// calculate the number of atoms at the current rate
	var atoms = msg.value * atomRate;

	// update the donor details
        record[_donor] += atoms;

	// only set return address on first donation
	if ( returnAddresses[_donor] == address(0x0) ) {
        	returnAddresses[_donor] = _returnAddress;
	}

	// update the totals
        totalEther += msg.value;
	totalAtom += atoms;

        Received(_donor, msg.value, atomRate);
    }

    /// Adjust the atomRate
    /// NOTE: Should we allow rate to adjust before the period starts?
    function adjustRate(uint newRate) only_admin only_during_period {
	atomRate = newRate;
    }

    /// Halt the contribution period. Any attempt at contributing will fail.
    function halt() only_admin only_during_period {
        isHalted = true;
        Halted();
    }

    /// Unhalt the contribution period.
    function unhalt() only_admin only_during_halted_period {
        isHalted = false;
        Unhalted();
    }

    /// Kill this contract.
    function kill() only_admin only_after_period {
        suicide(treasury);
    }
}


Makes all the fields public

Makes all the fields public

contract User {

    bytes32 public id;
    bytes32 public handle;
    bytes32 public email;
    bytes32 public name;
    bytes32 public score;
    bytes32 public teamId;

    bytes public tasks;

    // Constructor
    function User(
        bytes32 _id,
        bytes32 _handle,
        bytes32 _email,
        bytes32 _name,
        bytes32 _score,
        bytes32 _teamId
        ) {
            id = _id;
            handle = _handle;
            email = _email;
            name = _name;
            score = _score;
            teamId = _teamId;
    }
}

contract User {

    bytes32 id;
    bytes32 handle;
    bytes32 email;
    bytes32 name;
    bytes32 score;
    bytes32 teamId;

    // Constructor
    function User(
        bytes32 _id,
        bytes32 _handle,
        bytes32 _email,
        bytes32 _name,
        bytes32 _score,
        bytes32 _teamId
        ) {
            id = _id;
            handle = _handle;
            email = _email;
            name = _name;
            score = _score;
            teamId = _teamId;
    }
}


event LogAccountRefund

event LogAccountRefund

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

    mapping(string => Pot) nameToPot;
    mapping(address => uint) refunds;
    uint totalRefunds;

    event LogPotCreated(string name, uint buyIn, address indexed firstPlayer);
    event LogPotJoined(string name, address indexed newPlayer);
    event LogPotFull(string name);
    event LogPotExpired(string name);
    event LogPotClosed(string name);
    event LogPotWinner(string name, address indexed winner);
    event LogPotLoser(string name, address indexed loser);
    event LogAccountRefund(address indexed account, uint refundAmount);

    function PotOfGold() {
        owner = msg.sender;
    }

    function getTotal() constant returns (uint){
        require(msg.sender == owner);

        return this.balance - totalRefunds;
    }

    function ownerWithdraw(){
        require(msg.sender == owner);

        int toWithdraw = int(this.balance) - int(100 finney) - int(totalRefunds); // leave 0.1 ether for gas (?)
        require(toWithdraw > 0);

        owner.transfer(uint(toWithdraw));
    }

    function createPot(string name) payable {
        require(msg.value > 0); // must bet something
        require(bytes(name).length > 0); // name mustn't be empty
        require(nameToPot[name].buyIn == 0); // there isn't already a pot with this name

        Pot pot = nameToPot[name];
        pot.name = name;
        pot.buyIn = msg.value;
        pot.players.push(msg.sender);
        pot.isOpen = true;

        LogPotCreated(name, msg.value, msg.sender);
        LogPotJoined(name, msg.sender);
    }

    function joinPot(string name) payable {
        Pot pot = nameToPot[name];
        require(pot.isOpen); // pot exists and isn't over
        require(pot.players.length < 3); // pot isn't full
        require(msg.value == pot.buyIn); // must pay buyIn amount
        for(uint i = 0; i < pot.players.length; i++){
            require(pot.players[i] != msg.sender); //must be new to this pot
        }

        pot.players.push(msg.sender);
        LogPotJoined(name, msg.sender);

        if(pot.players.length == 3){
            pot.lastPlayerBlockNumber = block.number;
            LogPotFull(name);
        }
    }

    function canClosePot(string name) constant returns (bool){
        Pot pot = nameToPot[name];
        return pot.isOpen &&
                pot.players.length == 3 &&
                block.number > pot.lastPlayerBlockNumber + 1;
    }

    function closePot(string name){
        Pot pot = nameToPot[name];
        require(pot.isOpen); // pot isn't closed
        require(pot.players.length == 3); // pot full
        require(block.number > pot.lastPlayerBlockNumber + 1);

        pot.isOpen = false;
        LogPotClosed(name);

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - players didn't solve pot
            LogPotExpired(name);

            // return money minus 1% fee
            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += ((pot.buyIn * 99) / 100);
                totalRefunds += ((pot.buyIn * 99) / 100);
            }

            return;
        }

        bytes32 potShaResult = sha3(blockHash);
        uint8 loserIndex = uint8(uint256(potShaResult) % 3);

        address loser = pot.players[loserIndex];
        address winner1 = pot.players[(loserIndex + 1) % 3];
        address winner2 = pot.players[(loserIndex + 2) % 3];

        uint winAmount = ((pot.buyIn / 2) * 99) / 100; // split the buy-in and take 1% fee
        uint refundAmount = pot.buyIn + winAmount;

        refunds[winner1] += refundAmount;
        refunds[winner2] += refundAmount;
        totalRefunds += refundAmount * 2;

        LogPotWinner(name, winner1);
        LogPotWinner(name, winner2);
        LogPotLoser(name, loser);
    }

    function withdrawRefund(){
        uint refund = refunds[msg.sender];
        refunds[msg.sender] = 0;
        totalRefunds -= refund;

        msg.sender.transfer(refund);
        LogAccountRefund(msg.sender, refund);
    }
}

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

    mapping(string => Pot) nameToPot;
    mapping(address => uint) refunds;
    uint totalRefunds;

    event LogPotCreated(string name, uint buyIn, address indexed firstPlayer);
    event LogPotJoined(string name, address indexed newPlayer);
    event LogPotFull(string name);
    event LogPotExpired(string name);
    event LogPotClosed(string name);
    event LogPotWinner(string name, address indexed winner);
    event LogPotLoser(string name, address indexed loser);

    function PotOfGold() {
        owner = msg.sender;
    }

    function getTotal() constant returns (uint){
        require(msg.sender == owner);

        return this.balance - totalRefunds;
    }

    function ownerWithdraw(){
        require(msg.sender == owner);

        int toWithdraw = int(this.balance) - int(100 finney) - int(totalRefunds); // leave 0.1 ether for gas (?)
        require(toWithdraw > 0);

        owner.transfer(uint(toWithdraw));
    }

    function createPot(string name) payable {
        require(msg.value > 0); // must bet something
        require(bytes(name).length > 0); // name mustn't be empty
        require(nameToPot[name].buyIn == 0); // there isn't already a pot with this name

        Pot pot = nameToPot[name];
        pot.name = name;
        pot.buyIn = msg.value;
        pot.players.push(msg.sender);
        pot.isOpen = true;

        LogPotCreated(name, msg.value, msg.sender);
        LogPotJoined(name, msg.sender);
    }

    function joinPot(string name) payable {
        Pot pot = nameToPot[name];
        require(pot.isOpen); // pot exists and isn't over
        require(pot.players.length < 3); // pot isn't full
        require(msg.value == pot.buyIn); // must pay buyIn amount
        for(uint i = 0; i < pot.players.length; i++){
            require(pot.players[i] != msg.sender); //must be new to this pot
        }

        pot.players.push(msg.sender);
        LogPotJoined(name, msg.sender);

        if(pot.players.length == 3){
            pot.lastPlayerBlockNumber = block.number;
            LogPotFull(name);
        }
    }

    function canClosePot(string name) constant returns (bool){
        Pot pot = nameToPot[name];
        return pot.isOpen &&
                pot.players.length == 3 &&
                block.number > pot.lastPlayerBlockNumber + 1;
    }

    function closePot(string name){
        Pot pot = nameToPot[name];
        require(pot.isOpen); // pot isn't closed
        require(pot.players.length == 3); // pot full
        require(block.number > pot.lastPlayerBlockNumber + 1);

        pot.isOpen = false;
        LogPotClosed(name);

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - players didn't solve pot
            LogPotExpired(name);

            // return money minus 1% fee
            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += ((pot.buyIn * 99) / 100);
                totalRefunds += ((pot.buyIn * 99) / 100);
            }

            return;
        }

        bytes32 potShaResult = sha3(blockHash);
        uint8 loserIndex = uint8(uint256(potShaResult) % 3);

        address loser = pot.players[loserIndex];
        address winner1 = pot.players[(loserIndex + 1) % 3];
        address winner2 = pot.players[(loserIndex + 2) % 3];

        uint winAmount = ((pot.buyIn / 2) * 99) / 100; // split the buy-in and take 1% fee
        uint refundAmount = pot.buyIn + winAmount;

        refunds[winner1] += refundAmount;
        refunds[winner2] += refundAmount;
        totalRefunds += refundAmount * 2;

        LogPotWinner(name, winner1);
        LogPotWinner(name, winner2);
        LogPotLoser(name, loser);
    }

    function withdrawRefund() {
        uint refund = refunds[msg.sender];
        refunds[msg.sender] = 0;
        totalRefunds -= refund;

        msg.sender.transfer(refund); // this'll throw and restore state on failure
    }
}