expede/ethereum-status-codes

View on GitHub
contracts/FISSION.sol

Summary

Maintainability
Test Coverage
pragma solidity ^0.5.0;

import { LocalizationPreferences} from "/ethereum-localized-messaging/contracts/LocalizationPreferences.sol";
import { FissionLocalization } from "./localization/FissionLocalization.sol";

/**
 * @title The FISSION Status Code Library
 *
 * @dev Implementation of broadly applicable status codes for smart contracts.
 *      https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1066.md
 */
library FISSION {

    ///////////////////////////// Decomposed Enums /////////////////////////////

    /// @dev The category component of an ERC-1066 status code (ie: the columns)
    enum Category {
        Generic,
        Permission,
        Find,
        Negotiation,
        Availability,
        Finance,

        x60,
        x70,
        x80,
        x90,

        ApplicationSpecific,

        xB0,
        xC0,
        xD0,

        Cryptography,
        OffChain
    }

    /// @dev The reason component of an ERC-1066 status code (ie: the rows)
    enum Reason {
        Failure,
        Success,

        AwaitingOthers,
        Accepted,
        LowerLimit,
        ActionRequested,
        UpperLimit,

        x06,
        x07,

        Inapplicable,

        x09,
        x0A,
        x0B,
        x0C,
        x0D,
        x0E,

        Informational
    }

    //////////////////////////// Simple Status Enum ////////////////////////////

    /// @dev ERC-1066 status codes encoded as human-readable enums
    enum Status {

        // 0x0*
        Failure,
        Success,
        AwatingOthers,
        Accepted,
        LowerLimit,
        RecieverActionRequested,
        UpperLimit,
        RESERVEDx07,
        Inapplicable,
        RESERVEDx09,
        RESERVEDx0A,
        RESERVEDx0B,
        RESERVEDx0C,
        RESERVEDx0D,
        RESERVEDx0E,
        Informational,

        // 0x1*
        Disallowed_Stop,
        Allowed_Go,
        AwaitingOthersPermission,
        PermissionRequested,
        TooOpen_Insecure,
        NeedsYourPermission_RequestForContinuation,
        Revoked,
        RESERVEDx17,
        NotApplicatableToCurrentState,
        RESERVEDx19,
        RESERVEDx1A,
        RESERVEDx1B,
        RESERVEDx1C,
        RESERVEDx1D,
        RESERVEDx1E,
        PermissionDetails_ControlConditions,

        // 0x2*
        NotFound_Unequal_OutOfRange,
        Found_Equal_InRange,
        AwaitingMatch,
        MatchRequestSent,
        BelowRange_Underflow,
        RequestForMatch,
        Above_Range_Overflow,
        RESERVEDx27,
        Duplicate_Conflict_Collision,
        RESERVEDx29,
        RESERVEDx2A,
        RESERVEDx2B,
        RESERVEDx2C,
        RESERVEDx2D,
        RESERVEDx2E,
        MatchingInformation,

        // 0x3*
        SenderDisagrees_Nay,
        SenderAgrees_Yea,
        AwaitingRatification,
        OfferSent_Voted,
        QuorumNotReached,
        ReceiversRatificationRequested,
        OfferOrVoteLimitReached,
        RESERVEDx37,
        AlreadyVoted,
        RESERVEDx39,
        RESERVEDx3A,
        RESERVEDx3B,
        RESERVEDx3C,
        RESERVEDx3D,
        RESERVEDx3E,
        NegotiationRules_ParticipationInformation,

        // 0x4*
        Unavailable,
        Available,
        Paused,
        Queued,
        NotAvailableYet,
        AwaitingYourAvailability,
        Expired,
        RESERVEDx47,
        AlreadyDone,
        RESERVEDx49,
        RESERVEDx4A,
        RESERVEDx4B,
        RESERVEDx4C,
        RESERVEDx4D,
        RESERVEDx4E,
        AvailabilityRules_Information,

        // 0x5*
        TransferFailed,
        TransferSuccessful,
        AwaitingPaymentFromOthers,
        Hold_Escrow,
        InsufficientFunds,
        FundsRequested,
        TransferVolumeExceeded,
        RESERVEDx57,
        FundsNotRequired,
        RESERVEDx59,
        RESERVEDx5A,
        RESERVEDx5B,
        RESERVEDx5C,
        RESERVEDx5D,
        RESERVEDx5E,
        FinancialInformation,

        // 0x6*
        RESERVEDx60,
        RESERVEDx61,
        RESERVEDx62,
        RESERVEDx63,
        RESERVEDx64,
        RESERVEDx65,
        RESERVEDx66,
        RESERVEDx67,
        RESERVEDx68,
        RESERVEDx69,
        RESERVEDx6A,
        RESERVEDx6B,
        RESERVEDx6C,
        RESERVEDx6D,
        RESERVEDx6E,
        RESERVEDx6F,

        // 0x7*
        RESERVEDx70,
        RESERVEDx71,
        RESERVEDx72,
        RESERVEDx73,
        RESERVEDx74,
        RESERVEDx75,
        RESERVEDx76,
        RESERVEDx77,
        RESERVEDx78,
        RESERVEDx79,
        RESERVEDx7A,
        RESERVEDx7B,
        RESERVEDx7C,
        RESERVEDx7D,
        RESERVEDx7E,
        RESERVEDx7F,

        // 0x8*
        RESERVEDx80,
        RESERVEDx81,
        RESERVEDx82,
        RESERVEDx83,
        RESERVEDx84,
        RESERVEDx85,
        RESERVEDx86,
        RESERVEDx87,
        RESERVEDx88,
        RESERVEDx89,
        RESERVEDx8A,
        RESERVEDx8B,
        RESERVEDx8C,
        RESERVEDx8D,
        RESERVEDx8E,
        RESERVEDx8F,

        // 0x9*
        RESERVEDx90,
        RESERVEDx91,
        RESERVEDx92,
        RESERVEDx93,
        RESERVEDx94,
        RESERVEDx95,
        RESERVEDx96,
        RESERVEDx97,
        RESERVEDx98,
        RESERVEDx99,
        RESERVEDx9A,
        RESERVEDx9B,
        RESERVEDx9C,
        RESERVEDx9D,
        RESERVEDx9E,
        RESERVEDx9F,

        // 0xA*
        ApplicationSpecificFailure,
        ApplicationSpecificSuccess,
        ApplicationSpecificAwatingOthers,
        ApplicationSpecificAccepted,
        ApplicationSpecificLowerLimit,
        ApplicationSpecificRecieverActionRequested,
        ApplicationSpecificUpperLimit,
        RESERVEDxA7,
        ApplicationSpecific_Inapplicable,
        RESERVEDxA9,
        RESERVEDxAA,
        RESERVEDxAB,
        RESERVEDxAC,
        RESERVEDxAD,
        RESERVEDxAE,
        ApplicationSpecificInformational,

        // 0xB*
        RESERVEDxB0,
        RESERVEDxB1,
        RESERVEDxB2,
        RESERVEDxB3,
        RESERVEDxB4,
        RESERVEDxB5,
        RESERVEDxB6,
        RESERVEDxB7,
        RESERVEDxB8,
        RESERVEDxB9,
        RESERVEDxBA,
        RESERVEDxBB,
        RESERVEDxBC,
        RESERVEDxBD,
        RESERVEDxBE,
        RESERVEDxBF,

        // 0xC*
        RESERVEDxC0,
        RESERVEDxC1,
        RESERVEDxC2,
        RESERVEDxC3,
        RESERVEDxC4,
        RESERVEDxC5,
        RESERVEDxC6,
        RESERVEDxC7,
        RESERVEDxC8,
        RESERVEDxC9,
        RESERVEDxCA,
        RESERVEDxCB,
        RESERVEDxCC,
        RESERVEDxCD,
        RESERVEDxCE,
        RESERVEDxCF,

        // 0xD*
        RESERVEDxD0,
        RESERVEDxD1,
        RESERVEDxD2,
        RESERVEDxD3,
        RESERVEDxD4,
        RESERVEDxD5,
        RESERVEDxD6,
        RESERVEDxD7,
        RESERVEDxD8,
        RESERVEDxD9,
        RESERVEDxDA,
        RESERVEDxDB,
        RESERVEDxDC,
        RESERVEDxDD,
        RESERVEDxDE,
        RESERVEDxDF,

        // 0xE*
        DecryptFailure,
        DecryptSuccess,
        AwaitingOtherSignaturesOrKeys,
        Signed,
        Unsigned_Untrusted,
        SignatureRequired,
        KnownToBeCompromised,
        RESERVEDxE7,
        AlreadySigned_NotEncrypted,
        RESERVEDxE9,
        RESERVEDxEA,
        RESERVEDxEB,
        RESERVEDxEC,
        RESERVEDxED,
        RESERVEDxEE,
        Cryptography_ID_ProofMetadata,

        // 0xF*
        OffChainFailure,
        OffChainSuccess,
        AwatingOffChainProcess,
        OffChainProcessStarted,
        OffChainServiceUnreachable,
        OffChainActionRequired,
        OffChainExpiry_LimitReached,
        RESERVEDxF7,
        DuplicateOffChainRequest,
        RESERVEDxF9,
        RESERVEDxFA,
        RESERVEDxFB,
        RESERVEDxFC,
        RESERVEDxFD,
        RESERVEDxFE,
        OffChainInformation
    }

    ////////////////////////////// Construction ////////////////////////////////

    /**
     * @dev Coerce a status enum into a standard status byte
     *
     * @param statusEnum Status enum tag
     * @return status Binary ERC-1066 status code
     */
    function code(Status statusEnum) public pure returns (byte status) {
        return byte(uint8(statusEnum));
    }

    /**
     * @dev Construct a status code from a category and reason.
     *
     * @param category Category nibble
     * @param reason Reason nibble
     * @return status Binary ERC-1066 status code
     */
    function code(byte category, byte reason)
        public
        pure
        returns (byte status)
    {
        return (category << 4) | (byte(0x0F) & reason);
    }

    /**
     * @dev Construct a status code from a category and reason.
     *
     * @param category The category
     * @param reason The reason
     * @return status Binary ERC-1066 status code
     */
    function code(uint8 category, uint8 reason)
        public
        pure
        returns (byte status)
    {
        return byte(uint8((category << 4) + reason));
    }

    /**
     * @dev Construct a status code from category and reason enums
     *
     * @param category Category nibble
     * @param reason Reason nibble
     * @return status Binary ERC-1066 status code
     */
    function code(Category category, Reason reason)
        public
        pure
        returns (byte status)
    {
        return code(uint8(category), uint8(reason));
    }

    /**
     * @dev Construct an application-specific status code
     *
     * @param appReason Application-specific reason nibble
     * @return status Binary ERC-1066 status code
     */
    function appCode(byte appReason) public pure returns (byte status) {
        return (byte(0xA0) | appReason);
    }

    /**
     * @dev Construct an application-specific status code
     *
     * @param appReason Application-specific reason
     * @return status Binary ERC-1066 status code
     */
    function appCode(uint8 appReason) public pure returns (byte status) {
        return byte(160 + appReason);
    }

    /**
     * @dev Construct an application-specific status code
     *
     * @param appReason Application-specific reason enum
     * @return status Binary ERC-1066 status code
     */
    function appCode(Reason appReason) public pure returns (byte status) {
        return appCode(uint8(appReason));
    }

    /////////////////////////////// Get Nibbles ////////////////////////////////

    /**
     * @dev Extract the category from a status code
     *
     * @param status Binary ERC-1066 status code
     * @return category Category nibble
     */
    function categoryOf(byte status) public pure returns (byte category) {
        return status >> 4;
    }

    /**
     * @dev Extract the category from a status code enum
     *
     * @param status Status enum
     * @return category Category nibble
     */
    function categoryOf(Status status) public pure returns (byte category) {
        return categoryOf(byte(uint8(status)));
    }

    /**
     * @dev Extract the reason from a status code
     *
     * @param status Binary ERC-1066 status code
     * @return reason Reason nibble
     */
    function reasonOf(byte status) public pure returns (byte reason) {
        return status & 0x0F;
    }

    /**
     * @dev Extract the reason from a status code enum
     *
     * @param status Status enum
     * @return reason Reason nibble
     */
    function reasonOf(Status status) public pure returns (byte reason) {
        return reasonOf(byte(uint8(status)));
    }

    /**
     * @dev Decompose a status code into its category and reason nibbles
     *
     * @param status Binary ERC-1066 status code
     * @return {
     *   "category": "Category nibble",
     *   "reason": "Reason nibble"
     * }
     */
    function split(byte status) public returns (byte category, byte reason) {
        return (categoryOf(status), reasonOf(status));
    }

    ////////////////////////////// Localization ////////////////////////////////

    /**
     * @dev Lookup an ERC-1444 localization for a particular status code
     *
     * @param status Binary ERC-1066 status code
     * @param prefs The localization registry / proxy contract
     * @return {
     *   "found": "If the loicalization string was found (`false` if a fallback was used)",
     *   "message": "The localization string"
     * }
     */
    function localizeBy(byte status, LocalizationPreferences prefs)
        view
        public
        returns (bool found, string memory message)
    {
        return prefs.textFor(status);
    }

    ////////////////////////// Check Common Statuses ///////////////////////////

    /**
     * @dev Check if a status code is non-blocking (ie: an odd number)
     *
     * @param status Binary ERC-1066 status code
     * @return okay A boolean representing if the status code
     *         is okay / nonblocking
     */
    function isOk(byte status) public pure returns (bool okay) {
        return (uint8(status) % 2) == 1;
    }

    /**
     * @dev Check if a status code is blocking (ie: an even number)
     *
     * @param status Binary ERC-1066 status code
     * @return blocking A boolean representing if the status code is blocking
     */
    function isBlocking(byte status) public pure returns (bool blocking) {
        return !isOk(status);
    }

    /**
     * @dev Check if a status code represents success (ie: 0x*1)
     *
     * @param status Binary ERC-1066 status code
     * @return successful A boolean representing if the status code
     *         represents success
     */
    function isSuccess(byte status) public pure returns (bool successful) {
        return reasonOf(status) == 0x01;
    }

    /**
     * @dev Check if a status code represents failure (ie: 0x*0)
     *
     * @param status Binary ERC-1066 status code
     * @return failure A boolean representing if the status code
     *         represents failure
     */
    function isFailure(byte status) public pure returns (bool failure) {
        return reasonOf(status) == 0x00;
    }

    /**
     * @dev Check if a status code belongs to a particular category
     *
     * @param status Binary ERC-1066 status code
     * @param category Category nibble
     * @return belongs A boolean representing if the status code belongs to
     *         the target category
     */
    function isCategory(byte status, byte category)
        public
        pure
        returns (bool belongs)
    {
        return categoryOf(status) == category;
    }

    /**
     * @dev Check if a status code belongs to a particular category
     *
     * @param status Binary ERC-1066 status code
     * @param category Category enum
     * @return belongs A boolean representing if the status code belongs to
     *         the target category
     */
    function isCategory(byte status, Category category)
        public
        pure
        returns (bool belongs)
    {
        return categoryOf(status) == byte(uint8(category));
    }

    /**
     * @dev Check if a Status enum belongs to a particular category
     *
     * @param status Status enum
     * @param category Category enum
     * @return belongs A boolean representing if the status enum belongs to
     *         the target category
     */
    function isCategory(Status status, Category category)
        public
        pure
        returns (bool belongs)
    {
        return categoryOf(status) == byte(uint8(category));
    }

    /**
     * @dev Check if a status code has a particular reason
     *
     * @param status Binary ERC-1066 status code
     * @param reason Reason nibble
     * @return belongs A boolean representing if the status code has
     *         the target reason
     */
    function isReason(byte status, byte reason)
        public
        pure
        returns (bool belongs)
    {
        return reasonOf(status) == reason;
    }

    /**
     * @dev Check if a status code belongs to a particular category
     *
     * @param status Binary ERC-1066 status code
     * @param reason Reason enum
     * @return belongs A boolean representing if the status code has
     *         the target reason
     */
    function isReason(byte status, Reason reason)
        public
        pure
        returns (bool belongs)
    {
        return reasonOf(status) == byte(uint8(reason));
    }

    /**
     * @dev Check if a Status enum has a particular reason
     *
     * @param status Status enum
     * @param reason Reason enum
     * @return belongs A boolean representing if the status code has
     *         the target reason
     */
    function isReason(Status status, Reason reason)
        public
        pure
        returns (bool belongs)
    {
        return reasonOf(status) == byte(uint8(reason));
    }

    ///////////////////////////////// Requires /////////////////////////////////

    /**
     * @dev Require that a status code be nonblocking, otherwise `revert`
     *
     * @param status Binary ERC-1066 status code
     */
    function requireOk(byte status) public pure {
        require(isOk(status), "Blocking status code"); // TODO: use translation singleton
    }

    /**
     * @dev Require that a status code be nonblocking,
     *      otherwise `revert` with message
     *
     * @param status Binary ERC-1066 status code
     * @param message Revert message
     */
    function requireOk(byte status, string memory message) public pure {
        require(isOk(status), message);
    }

    /**
     * @dev Require that a status code be nonblocking,
     *      otherwise `revert` with an ERC-1444 automatically localized message
     *
     * @param status Binary ERC-1066 status code
     * @param prefs Localization preference registry
     */
    function requireOk(byte status, LocalizationPreferences prefs) public view {
        (bool _, string memory message) = localizeBy(status, prefs);
        requireOk(status, message);
    }

    /**
     * @dev Require that a status code represent success, otherwise `revert`
     *
     * @param status Binary ERC-1066 status code
     */
    function requireSuccess(byte status) public pure {
        require(isSuccess(status), "Unsuccessful status code reason");
    }

    /**
     * @dev Require that a status code represent success,
     *      otherwise `revert` with message
     *
     * @param status Binary ERC-1066 status code
     * @param message Revert message
     */
    function requireSuccess(byte status, string memory message) public pure {
        require(isSuccess(status), message);
    }

    /**
     * @dev Require that a status code represent success,
     *      otherwise `revert` with an ERC-1444 automatically localized message
     *
     * @param status Binary ERC-1066 status code
     * @param prefs Localization preference registry
     */
    function requireSuccess(byte status, LocalizationPreferences prefs)
        public
        view
    {
        (bool _, string memory message) = localizeBy(status, prefs);
        requireSuccess(status, message);
    }

    /**
     * @dev Require that a status code belongs to a particular category,
     *      otherwise `revert`
     *
     * @param status Binary ERC-1066 status code
     * @param category Required category nibble
     */
    function requireCategory(byte status, byte category) public view {
        require(isCategory(status, category));
    }

    /**
     * @dev Require that a status code belongs to a particular category,
     *      otherwise `revert` with message
     *
     * @param status Binary ERC-1066 status code
     * @param category Required category nibble
     * @param message Revert message
     */
    function requireCategory(byte status, byte category, string memory message)
        public
        view
    {
        require(isCategory(status, category), message);
    }

    /**
     * @dev Require that a status code belongs to a particular category,
     *      otherwise `revert` with an ERC-1444 automatically localized message
     *
     * @param status Binary ERC-1066 status code
     * @param category Required category nibble
     * @param prefs Localization preference registry
     */
    function requireCategory(
        byte status,
        byte category,
        LocalizationPreferences prefs
    )
        public
        view
    {
        (bool _, string memory message) = localizeBy(status, prefs);
        requireCategory(status, category, message);
    }

    /**
     * @dev Require that a status code has a particular reason,
     *      otherwise `revert`
     *
     * @param status Binary ERC-1066 status code
     * @param reason Required reason nibble
     */
    function requireReason(byte status, byte reason) public view {
        require(isReason(status, reason));
    }

    /**
     * @dev Require that a status code has a particular reason,
     *      otherwise `revert` with message
     *
     * @param status Binary ERC-1066 status code
     * @param reason Required reason nibble
     * @param message Revert message
     */
    function requireReason(byte status, byte reason, string memory message)
        public
        view
    {
        require(isReason(status, reason), message);
    }

    /**
     * @dev Require that a status code has a particular reason,
     *      otherwise `revert` with an ERC-1444 automatically localized message
     *
     * @param status Binary ERC-1066 status code
     * @param reason Required reason nibble
     * @param prefs Localization preference registry
     */
    function requireReason(
        byte status,
        byte reason,
        LocalizationPreferences prefs
    )
        public
        view
    {
        (bool _, string memory message) = localizeBy(status, prefs);
        requireReason(status, reason, message);
    }
}