creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/SpectraForwarder.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.20;\n\nimport {Forwarder} from \"@opengsn/contracts/src/forwarder/Forwarder.sol\";\n\ncontract SpectraForwarder is Forwarder {\n    constructor() Forwarder() {}\n}\n"
      },
      "lib/gsn/packages/contracts/src/forwarder/Forwarder.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.0;\npragma abicoder v2;\n\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport \"./IForwarder.sol\";\n\ncontract Forwarder is IForwarder {\n    using ECDSA for bytes32;\n\n    string public constant GENERIC_PARAMS = \"address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data,uint256 validUntil\";\n\n    string public constant EIP712_DOMAIN_TYPE = \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\";\n\n    mapping(bytes32 => bool) public typeHashes;\n    mapping(bytes32 => bool) public domains;\n\n    // Nonces of senders, used to prevent replay attacks\n    mapping(address => uint256) private nonces;\n\n    // solhint-disable-next-line no-empty-blocks\n    receive() external payable {}\n\n    function getNonce(address from)\n    public view override\n    returns (uint256) {\n        return nonces[from];\n    }\n\n    constructor() {\n\n        string memory requestType = string(abi.encodePacked(\"ForwardRequest(\", GENERIC_PARAMS, \")\"));\n        registerRequestTypeInternal(requestType);\n    }\n\n    function verify(\n        ForwardRequest calldata req,\n        bytes32 domainSeparator,\n        bytes32 requestTypeHash,\n        bytes calldata suffixData,\n        bytes calldata sig)\n    external override view {\n\n        _verifyNonce(req);\n        _verifySig(req, domainSeparator, requestTypeHash, suffixData, sig);\n    }\n\n    function execute(\n        ForwardRequest calldata req,\n        bytes32 domainSeparator,\n        bytes32 requestTypeHash,\n        bytes calldata suffixData,\n        bytes calldata sig\n    )\n    external payable\n    override\n    returns (bool success, bytes memory ret) {\n        _verifySig(req, domainSeparator, requestTypeHash, suffixData, sig);\n        _verifyAndUpdateNonce(req);\n\n        require(req.validUntil == 0 || req.validUntil > block.number, \"FWD: request expired\");\n\n        uint gasForTransfer = 0;\n        if ( req.value != 0 ) {\n            gasForTransfer = 40000; //buffer in case we need to move eth after the transaction.\n        }\n        bytes memory callData = abi.encodePacked(req.data, req.from);\n        require(gasleft()*63/64 >= req.gas + gasForTransfer, \"FWD: insufficient gas\");\n        // solhint-disable-next-line avoid-low-level-calls\n        (success,ret) = req.to.call{gas : req.gas, value : req.value}(callData);\n        if ( req.value != 0 && address(this).balance>0 ) {\n            // can't fail: req.from signed (off-chain) the request, so it must be an EOA...\n            payable(req.from).transfer(address(this).balance);\n        }\n\n        return (success,ret);\n    }\n\n\n    function _verifyNonce(ForwardRequest calldata req) internal view {\n        require(nonces[req.from] == req.nonce, \"FWD: nonce mismatch\");\n    }\n\n    function _verifyAndUpdateNonce(ForwardRequest calldata req) internal {\n        require(nonces[req.from]++ == req.nonce, \"FWD: nonce mismatch\");\n    }\n\n    function registerRequestType(string calldata typeName, string calldata typeSuffix) external override {\n\n        for (uint i = 0; i < bytes(typeName).length; i++) {\n            bytes1 c = bytes(typeName)[i];\n            require(c != \"(\" && c != \")\", \"FWD: invalid typename\");\n        }\n\n        string memory requestType = string(abi.encodePacked(typeName, \"(\", GENERIC_PARAMS, \",\", typeSuffix));\n        registerRequestTypeInternal(requestType);\n    }\n\n    function registerDomainSeparator(string calldata name, string calldata version) external override {\n        uint256 chainId;\n        /* solhint-disable-next-line no-inline-assembly */\n        assembly { chainId := chainid() }\n\n        bytes memory domainValue = abi.encode(\n            keccak256(bytes(EIP712_DOMAIN_TYPE)),\n            keccak256(bytes(name)),\n            keccak256(bytes(version)),\n            chainId,\n            address(this));\n\n        bytes32 domainHash = keccak256(domainValue);\n\n        domains[domainHash] = true;\n        emit DomainRegistered(domainHash, domainValue);\n    }\n\n    function registerRequestTypeInternal(string memory requestType) internal {\n\n        bytes32 requestTypehash = keccak256(bytes(requestType));\n        typeHashes[requestTypehash] = true;\n        emit RequestTypeRegistered(requestTypehash, requestType);\n    }\n\n    function _verifySig(\n        ForwardRequest calldata req,\n        bytes32 domainSeparator,\n        bytes32 requestTypeHash,\n        bytes calldata suffixData,\n        bytes calldata sig)\n    internal\n    view\n    {\n        require(domains[domainSeparator], \"FWD: unregistered domain sep.\");\n        require(typeHashes[requestTypeHash], \"FWD: unregistered typehash\");\n        bytes32 digest = keccak256(abi.encodePacked(\n                \"\\x19\\x01\", domainSeparator,\n                keccak256(_getEncoded(req, requestTypeHash, suffixData))\n            ));\n        require(digest.recover(sig) == req.from, \"FWD: signature mismatch\");\n    }\n\n    function _getEncoded(\n        ForwardRequest calldata req,\n        bytes32 requestTypeHash,\n        bytes calldata suffixData\n    )\n    public\n    pure\n    returns (\n        bytes memory\n    ) {\n        // we use encodePacked since we append suffixData as-is, not as dynamic param.\n        // still, we must make sure all first params are encoded as abi.encode()\n        // would encode them - as 256-bit-wide params.\n        return abi.encodePacked(\n            requestTypeHash,\n            uint256(uint160(req.from)),\n            uint256(uint160(req.to)),\n            req.value,\n            req.gas,\n            req.nonce,\n            keccak256(req.data),\n            req.validUntil,\n            suffixData\n        );\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n    enum RecoverError {\n        NoError,\n        InvalidSignature,\n        InvalidSignatureLength,\n        InvalidSignatureS\n    }\n\n    /**\n     * @dev The signature derives the `address(0)`.\n     */\n    error ECDSAInvalidSignature();\n\n    /**\n     * @dev The signature has an invalid length.\n     */\n    error ECDSAInvalidSignatureLength(uint256 length);\n\n    /**\n     * @dev The signature has an S value that is in the upper half order.\n     */\n    error ECDSAInvalidSignatureS(bytes32 s);\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not\n     * return address(0) without also returning an error description. Errors are documented using an enum (error type)\n     * and a bytes32 providing additional information about the error.\n     *\n     * If no error is returned, then the address can be used for verification purposes.\n     *\n     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.\n     *\n     * Documentation for signature generation:\n     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n     */\n    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {\n        if (signature.length == 65) {\n            bytes32 r;\n            bytes32 s;\n            uint8 v;\n            // ecrecover takes the signature parameters, and the only way to get them\n            // currently is to use assembly.\n            /// @solidity memory-safe-assembly\n            assembly {\n                r := mload(add(signature, 0x20))\n                s := mload(add(signature, 0x40))\n                v := byte(0, mload(add(signature, 0x60)))\n            }\n            return tryRecover(hash, v, r, s);\n        } else {\n            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));\n        }\n    }\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature`. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.\n     */\n    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);\n        _throwError(error, errorArg);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n     *\n     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n     */\n    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {\n        unchecked {\n            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n            // We do not check for an overflow here since the shift operation results in 0 or 1.\n            uint8 v = uint8((uint256(vs) >> 255) + 27);\n            return tryRecover(hash, v, r, s);\n        }\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n     */\n    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {\n        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);\n        _throwError(error, errorArg);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     */\n    function tryRecover(\n        bytes32 hash,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal pure returns (address, RecoverError, bytes32) {\n        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n        // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n        //\n        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n        // these malleable signatures as well.\n        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n            return (address(0), RecoverError.InvalidSignatureS, s);\n        }\n\n        // If the signature is valid (and not malleable), return the signer address\n        address signer = ecrecover(hash, v, r, s);\n        if (signer == address(0)) {\n            return (address(0), RecoverError.InvalidSignature, bytes32(0));\n        }\n\n        return (signer, RecoverError.NoError, bytes32(0));\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     */\n    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {\n        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);\n        _throwError(error, errorArg);\n        return recovered;\n    }\n\n    /**\n     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.\n     */\n    function _throwError(RecoverError error, bytes32 errorArg) private pure {\n        if (error == RecoverError.NoError) {\n            return; // no error: do nothing\n        } else if (error == RecoverError.InvalidSignature) {\n            revert ECDSAInvalidSignature();\n        } else if (error == RecoverError.InvalidSignatureLength) {\n            revert ECDSAInvalidSignatureLength(uint256(errorArg));\n        } else if (error == RecoverError.InvalidSignatureS) {\n            revert ECDSAInvalidSignatureS(errorArg);\n        }\n    }\n}\n"
      },
      "lib/gsn/packages/contracts/src/forwarder/IForwarder.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity >=0.7.6;\npragma abicoder v2;\n\ninterface IForwarder {\n\n    struct ForwardRequest {\n        address from;\n        address to;\n        uint256 value;\n        uint256 gas;\n        uint256 nonce;\n        bytes data;\n        uint256 validUntil;\n    }\n\n    event DomainRegistered(bytes32 indexed domainSeparator, bytes domainValue);\n\n    event RequestTypeRegistered(bytes32 indexed typeHash, string typeStr);\n\n    function getNonce(address from)\n    external view\n    returns(uint256);\n\n    /**\n     * verify the transaction would execute.\n     * validate the signature and the nonce of the request.\n     * revert if either signature or nonce are incorrect.\n     * also revert if domainSeparator or requestTypeHash are not registered.\n     */\n    function verify(\n        ForwardRequest calldata forwardRequest,\n        bytes32 domainSeparator,\n        bytes32 requestTypeHash,\n        bytes calldata suffixData,\n        bytes calldata signature\n    ) external view;\n\n    /**\n     * execute a transaction\n     * @param forwardRequest - all transaction parameters\n     * @param domainSeparator - domain used when signing this request\n     * @param requestTypeHash - request type used when signing this request.\n     * @param suffixData - the extension data used when signing this request.\n     * @param signature - signature to validate.\n     *\n     * the transaction is verified, and then executed.\n     * the success and ret of \"call\" are returned.\n     * This method would revert only verification errors. target errors\n     * are reported using the returned \"success\" and ret string\n     */\n    function execute(\n        ForwardRequest calldata forwardRequest,\n        bytes32 domainSeparator,\n        bytes32 requestTypeHash,\n        bytes calldata suffixData,\n        bytes calldata signature\n    )\n    external payable\n    returns (bool success, bytes memory ret);\n\n    /**\n     * Register a new Request typehash.\n     * @param typeName - the name of the request type.\n     * @param typeSuffix - any extra data after the generic params.\n     *  (must add at least one param. The generic ForwardRequest type is always registered by the constructor)\n     */\n    function registerRequestType(string calldata typeName, string calldata typeSuffix) external;\n\n    /**\n     * Register a new domain separator.\n     * The domain separator must have the following fields: name,version,chainId, verifyingContract.\n     * the chainId is the current network's chainId, and the verifyingContract is this forwarder.\n     * This method is given the domain name and version to create and register the domain separator value.\n     * @param name the domain's display name\n     * @param version the domain/protocol version\n     */\n    function registerDomainSeparator(string calldata name, string calldata version) external;\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "forge-std/=lib/forge-std/src/",
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@opengsn/=lib/gsn/packages/",
        "utils/=test/utils/",
        "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
        "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
        "gsn/=lib/gsn/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "viaIR": false,
      "libraries": {}
    }
  }}