65 lines
53 KiB
JSON
65 lines
53 KiB
JSON
{
|
|
"language": "Solidity",
|
|
"sources": {
|
|
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n"
|
|
},
|
|
"@openzeppelin/contracts/utils/math/Math.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(\n uint256 x,\n uint256 y,\n uint256 denominator,\n Rounding rounding\n ) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10**64) {\n value /= 10**64;\n result += 64;\n }\n if (value >= 10**32) {\n value /= 10**32;\n result += 32;\n }\n if (value >= 10**16) {\n value /= 10**16;\n result += 16;\n }\n if (value >= 10**8) {\n value /= 10**8;\n result += 8;\n }\n if (value >= 10**4) {\n value /= 10**4;\n result += 4;\n }\n if (value >= 10**2) {\n value /= 10**2;\n result += 2;\n }\n if (value >= 10**1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n }\n }\n}\n"
|
|
},
|
|
"@openzeppelin/contracts/utils/structs/EnumerableSet.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastValue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastValue;\n // Update the index for the moved value\n set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n bytes32[] memory store = _values(set._inner);\n bytes32[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n}\n"
|
|
},
|
|
"contracts/Configuration.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nstruct MarketplaceConfig {\n CollateralConfig collateral;\n ProofConfig proofs;\n}\n\nstruct CollateralConfig {\n /// @dev percentage of remaining collateral slot after it has been freed\n /// (equivalent to `collateral - (collateral*maxNumberOfSlashes*slashPercentage)/100`)\n /// TODO: to be aligned more closely with actual cost of repair once bandwidth incentives are known,\n /// see https://github.com/codex-storage/codex-contracts-eth/pull/47#issuecomment-1465511949.\n uint8 repairRewardPercentage;\n\n uint8 maxNumberOfSlashes; // frees slot when the number of slashing reaches this value\n uint16 slashCriterion; // amount of proofs missed that lead to slashing\n uint8 slashPercentage; // percentage of the collateral that is slashed\n}\n\nstruct ProofConfig {\n uint256 period; // proofs requirements are calculated per period (in seconds)\n uint256 timeout; // mark proofs as missing before the timeout (in seconds)\n uint8 downtime; // ignore this much recent blocks for proof requirements\n}\n"
|
|
},
|
|
"contracts/Marketplace.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\nimport \"./Configuration.sol\";\nimport \"./Requests.sol\";\nimport \"./Proofs.sol\";\nimport \"./StateRetrieval.sol\";\n\ncontract Marketplace is Proofs, StateRetrieval {\n using EnumerableSet for EnumerableSet.Bytes32Set;\n using Requests for Request;\n\n IERC20 public immutable token;\n MarketplaceConfig public config;\n\n MarketplaceFunds private _funds;\n mapping(RequestId => Request) private _requests;\n mapping(RequestId => RequestContext) private _requestContexts;\n mapping(SlotId => Slot) internal _slots;\n\n struct RequestContext {\n RequestState state;\n uint256 slotsFilled;\n uint256 startedAt;\n uint256 endsAt;\n }\n\n struct Slot {\n SlotState state;\n RequestId requestId;\n uint256 slotIndex;\n\n /// @notice Tracks the current amount of host's collateral that is to be payed out at the end of Slot's lifespan.\n /// @dev When Slot is filled, the collateral is collected in amount of request.ask.collateral\n /// @dev When Host is slashed for missing a proof the slashed amount is reflected in this variable\n uint256 currentCollateral;\n address host;\n }\n\n struct ActiveSlot {\n Request request;\n uint256 slotIndex;\n }\n\n constructor(\n IERC20 token_,\n MarketplaceConfig memory configuration\n ) Proofs(configuration.proofs) marketplaceInvariant {\n token = token_;\n\n require(configuration.collateral.repairRewardPercentage <= 100, \"Must be less than 100\");\n require(configuration.collateral.slashPercentage <= 100, \"Must be less than 100\");\n require(configuration.collateral.maxNumberOfSlashes * configuration.collateral.slashPercentage <= 100, \"Total slash percentage must be less then 100\");\n config = configuration;\n }\n\n function requestStorage(\n Request calldata request\n ) public marketplaceInvariant {\n require(request.client == msg.sender, \"Invalid client address\");\n\n RequestId id = request.id();\n require(_requests[id].client == address(0), \"Request already exists\");\n\n _requests[id] = request;\n _requestContexts[id].endsAt = block.timestamp + request.ask.duration;\n\n _addToMyRequests(request.client, id);\n\n uint256 amount = request.price();\n _funds.received += amount;\n _funds.balance += amount;\n _transferFrom(msg.sender, amount);\n\n emit StorageRequested(id, request.ask);\n }\n\n function fillSlot(\n RequestId requestId,\n uint256 slotIndex,\n bytes calldata proof\n ) public requestIsKnown(requestId) {\n Request storage request = _requests[requestId];\n require(slotIndex < request.ask.slots, \"Invalid slot\");\n\n SlotId slotId = Requests.slotId(requestId, slotIndex);\n Slot storage slot = _slots[slotId];\n slot.requestId = requestId;\n slot.slotIndex = slotIndex;\n\n require(slotState(slotId) == SlotState.Free, \"Slot is not free\");\n\n _startRequiringProofs(slotId, request.ask.proofProbability);\n submitProof(slotId, proof);\n\n slot.host = msg.sender;\n slot.state = SlotState.Filled;\n RequestContext storage context = _requestContexts[requestId];\n context.slotsFilled += 1;\n\n // Collect collateral\n uint256 collateralAmount = request.ask.collateral;\n _transferFrom(msg.sender, collateralAmount);\n _funds.received += collateralAmount;\n _funds.balance += collateralAmount;\n slot.currentCollateral = collateralAmount;\n\n _addToMySlots(slot.host, slotId);\n\n emit SlotFilled(requestId, slotIndex, slotId);\n if (context.slotsFilled == request.ask.slots) {\n context.state = RequestState.Started;\n context.startedAt = block.timestamp;\n emit RequestFulfilled(requestId);\n }\n }\n\n function freeSlot(SlotId slotId) public slotIsNotFree(slotId) {\n Slot storage slot = _slots[slotId];\n require(slot.host == msg.sender, \"Slot filled by other host\");\n SlotState state = slotState(slotId);\n require(state != SlotState.Paid, \"Already paid\");\n\n if (state == SlotState.Finished) {\n _payoutSlot(slot.requestId, slotId);\n } else if (state == SlotState.Failed) {\n _removeFromMySlots(msg.sender, slotId);\n } else if (state == SlotState.Filled) {\n _forciblyFreeSlot(slotId);\n }\n }\n\n function markProofAsMissing(SlotId slotId, Period period) public {\n require(slotState(slotId) == SlotState.Filled, \"Slot not accepting proofs\");\n _markProofAsMissing(slotId, period);\n Slot storage slot = _slots[slotId];\n Request storage request = _requests[slot.requestId];\n\n if (missingProofs(slotId) % config.collateral.slashCriterion == 0) {\n uint256 slashedAmount = (request.ask.collateral * config.collateral.slashPercentage) / 100;\n slot.currentCollateral -= slashedAmount;\n _funds.slashed += slashedAmount;\n _funds.balance -= slashedAmount;\n\n if (missingProofs(slotId) / config.collateral.slashCriterion >= config.collateral.maxNumberOfSlashes) {\n // When the number of slashings is at or above the allowed amount,\n // free the slot.\n _forciblyFreeSlot(slotId);\n }\n }\n }\n\n function _forciblyFreeSlot(SlotId slotId) internal marketplaceInvariant {\n Slot storage slot = _slots[slotId];\n RequestId requestId = slot.requestId;\n RequestContext storage context = _requestContexts[requestId];\n\n _removeFromMySlots(slot.host, slotId);\n\n delete _slots[slotId];\n context.slotsFilled -= 1;\n emit SlotFreed(requestId, slotId);\n resetMissingProofs(slotId);\n\n Request storage request = _requests[requestId];\n uint256 slotsLost = request.ask.slots - context.slotsFilled;\n if (\n slotsLost > request.ask.maxSlotLoss &&\n context.state == RequestState.Started\n ) {\n context.state = RequestState.Failed;\n context.endsAt = block.timestamp - 1;\n emit RequestFailed(requestId);\n\n // TODO: send client remaining funds\n }\n }\n\n function _payoutSlot(\n RequestId requestId,\n SlotId slotId\n ) private requestIsKnown(requestId) marketplaceInvariant {\n RequestContext storage context = _requestContexts[requestId];\n Request storage request = _requests[requestId];\n context.state = RequestState.Finished;\n _removeFromMyRequests(request.client, requestId);\n Slot storage slot = _slots[slotId];\n\n _removeFromMySlots(slot.host, slotId);\n\n uint256 amount = _requests[requestId].pricePerSlot() + slot.currentCollateral;\n _funds.sent += amount;\n _funds.balance -= amount;\n slot.state = SlotState.Paid;\n require(token.transfer(slot.host, amount), \"Payment failed\");\n }\n\n /// @notice Withdraws storage request funds back to the client that deposited them.\n /// @dev Request must be expired, must be in RequestState.New, and the transaction must originate from the depositer address.\n /// @param requestId the id of the request\n function withdrawFunds(RequestId requestId) public marketplaceInvariant {\n Request storage request = _requests[requestId];\n require(block.timestamp > request.expiry, \"Request not yet timed out\");\n require(request.client == msg.sender, \"Invalid client address\");\n RequestContext storage context = _requestContexts[requestId];\n require(context.state == RequestState.New, \"Invalid state\");\n\n // Update request state to Cancelled. Handle in the withdraw transaction\n // as there needs to be someone to pay for the gas to update the state\n context.state = RequestState.Cancelled;\n _removeFromMyRequests(request.client, requestId);\n\n emit RequestCancelled(requestId);\n\n // TODO: To be changed once we start paying out hosts for the time they\n // fill a slot. The amount that we paid to hosts will then have to be\n // deducted from the price.\n uint256 amount = request.price();\n _funds.sent += amount;\n _funds.balance -= amount;\n require(token.transfer(msg.sender, amount), \"Withdraw failed\");\n }\n\n function getActiveSlot(SlotId slotId)\n public\n view\n slotIsNotFree(slotId)\n returns (ActiveSlot memory)\n {\n Slot storage slot = _slots[slotId];\n ActiveSlot memory activeSlot;\n activeSlot.request = _requests[slot.requestId];\n activeSlot.slotIndex = slot.slotIndex;\n return activeSlot;\n }\n\n modifier requestIsKnown(RequestId requestId) {\n require(_requests[requestId].client != address(0), \"Unknown request\");\n _;\n }\n\n function getRequest(\n RequestId requestId\n ) public view requestIsKnown(requestId) returns (Request memory) {\n return _requests[requestId];\n }\n\n modifier slotIsNotFree(SlotId slotId) {\n require(_slots[slotId].state != SlotState.Free, \"Slot is free\");\n _;\n }\n\n function requestEnd(RequestId requestId) public view returns (uint256) {\n uint256 end = _requestContexts[requestId].endsAt;\n RequestState state = requestState(requestId);\n if (state == RequestState.New || state == RequestState.Started) {\n return end;\n } else {\n return Math.min(end, block.timestamp - 1);\n }\n }\n\n function getHost(SlotId slotId) public view returns (address) {\n return _slots[slotId].host;\n }\n\n function requestState(\n RequestId requestId\n ) public view requestIsKnown(requestId) returns (RequestState) {\n RequestContext storage context = _requestContexts[requestId];\n if (\n context.state == RequestState.New &&\n block.timestamp > _requests[requestId].expiry\n ) {\n return RequestState.Cancelled;\n } else if (\n context.state == RequestState.Started && block.timestamp > context.endsAt\n ) {\n return RequestState.Finished;\n } else {\n return context.state;\n }\n }\n\n function slotState(SlotId slotId) public view override returns (SlotState) {\n Slot storage slot = _slots[slotId];\n if (RequestId.unwrap(slot.requestId) == 0) {\n return SlotState.Free;\n }\n RequestState reqState = requestState(slot.requestId);\n if (slot.state == SlotState.Paid) {\n return SlotState.Paid;\n }\n if (reqState == RequestState.Cancelled) {\n return SlotState.Finished;\n }\n if (reqState == RequestState.Finished) {\n return SlotState.Finished;\n }\n if (reqState == RequestState.Failed) {\n return SlotState.Failed;\n }\n return slot.state;\n }\n\n function _transferFrom(address sender, uint256 amount) internal {\n address receiver = address(this);\n require(token.transferFrom(sender, receiver, amount), \"Transfer failed\");\n }\n\n event StorageRequested(RequestId requestId, Ask ask);\n event RequestFulfilled(RequestId indexed requestId);\n event RequestFailed(RequestId indexed requestId);\n event SlotFilled(\n RequestId indexed requestId,\n uint256 indexed slotIndex,\n SlotId slotId\n );\n event SlotFreed(RequestId indexed requestId, SlotId slotId);\n event RequestCancelled(RequestId indexed requestId);\n\n modifier marketplaceInvariant() {\n MarketplaceFunds memory oldFunds = _funds;\n _;\n assert(_funds.received >= oldFunds.received);\n assert(_funds.sent >= oldFunds.sent);\n assert(_funds.slashed >= oldFunds.slashed);\n assert(_funds.received == _funds.balance + _funds.sent + _funds.slashed);\n }\n\n struct MarketplaceFunds {\n uint256 balance;\n uint256 received;\n uint256 sent;\n uint256 slashed;\n }\n}\n"
|
|
},
|
|
"contracts/Periods.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\ncontract Periods {\n type Period is uint256;\n\n uint256 internal immutable _secondsPerPeriod;\n\n constructor(uint256 secondsPerPeriod) {\n _secondsPerPeriod = secondsPerPeriod;\n }\n\n function _periodOf(uint256 timestamp) internal view returns (Period) {\n return Period.wrap(timestamp / _secondsPerPeriod);\n }\n\n function _blockPeriod() internal view returns (Period) {\n return _periodOf(block.timestamp);\n }\n\n function _nextPeriod(Period period) internal pure returns (Period) {\n return Period.wrap(Period.unwrap(period) + 1);\n }\n\n function _periodStart(Period period) internal view returns (uint256) {\n return Period.unwrap(period) * _secondsPerPeriod;\n }\n\n function _periodEnd(Period period) internal view returns (uint256) {\n return _periodStart(_nextPeriod(period));\n }\n\n function _isBefore(Period a, Period b) internal pure returns (bool) {\n return Period.unwrap(a) < Period.unwrap(b);\n }\n\n function _isAfter(Period a, Period b) internal pure returns (bool) {\n return _isBefore(b, a);\n }\n}\n"
|
|
},
|
|
"contracts/Proofs.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport \"./Configuration.sol\";\nimport \"./Requests.sol\";\nimport \"./Periods.sol\";\n\nabstract contract Proofs is Periods {\n ProofConfig private _config;\n\n constructor(ProofConfig memory config) Periods(config.period) {\n require(block.number > 256, \"Insufficient block height\");\n _config = config;\n }\n\n mapping(SlotId => uint256) private _slotStarts;\n mapping(SlotId => uint256) private _probabilities;\n mapping(SlotId => uint256) private _missed;\n mapping(SlotId => mapping(Period => bool)) private _received;\n mapping(SlotId => mapping(Period => bool)) private _missing;\n\n function slotState(SlotId id) public view virtual returns (SlotState);\n\n function missingProofs(SlotId slotId) public view returns (uint256) {\n return _missed[slotId];\n }\n\n function resetMissingProofs(SlotId slotId) internal {\n _missed[slotId] = 0;\n }\n\n function _startRequiringProofs(SlotId id, uint256 probability) internal {\n _slotStarts[id] = block.timestamp;\n _probabilities[id] = probability;\n }\n\n function _getPointer(SlotId id, Period period) internal view returns (uint8) {\n uint256 blockNumber = block.number % 256;\n uint256 periodNumber = Period.unwrap(period) % 256;\n uint256 idOffset = uint256(SlotId.unwrap(id)) % 256;\n uint256 pointer = (blockNumber + periodNumber + idOffset) % 256;\n return uint8(pointer);\n }\n\n function getPointer(SlotId id) public view returns (uint8) {\n return _getPointer(id, _blockPeriod());\n }\n\n function _getChallenge(uint8 pointer) internal view returns (bytes32) {\n bytes32 hash = blockhash(block.number - 1 - pointer);\n assert(uint256(hash) != 0);\n return keccak256(abi.encode(hash));\n }\n\n function _getChallenge(\n SlotId id,\n Period period\n ) internal view returns (bytes32) {\n return _getChallenge(_getPointer(id, period));\n }\n\n function getChallenge(SlotId id) public view returns (bytes32) {\n return _getChallenge(id, _blockPeriod());\n }\n\n function _getProofRequirement(\n SlotId id,\n Period period\n ) internal view returns (bool isRequired, uint8 pointer) {\n SlotState state = slotState(id);\n Period start = _periodOf(_slotStarts[id]);\n if (state != SlotState.Filled || !_isAfter(period, start)) {\n return (false, 0);\n }\n pointer = _getPointer(id, period);\n bytes32 challenge = _getChallenge(pointer);\n uint256 probability = (_probabilities[id] * (256 - _config.downtime)) / 256;\n isRequired = probability == 0 || uint256(challenge) % probability == 0;\n }\n\n function _isProofRequired(\n SlotId id,\n Period period\n ) internal view returns (bool) {\n bool isRequired;\n uint8 pointer;\n (isRequired, pointer) = _getProofRequirement(id, period);\n return isRequired && pointer >= _config.downtime;\n }\n\n function isProofRequired(SlotId id) public view returns (bool) {\n return _isProofRequired(id, _blockPeriod());\n }\n\n function willProofBeRequired(SlotId id) public view returns (bool) {\n bool isRequired;\n uint8 pointer;\n (isRequired, pointer) = _getProofRequirement(id, _blockPeriod());\n return isRequired && pointer < _config.downtime;\n }\n\n function submitProof(SlotId id, bytes calldata proof) public {\n require(proof.length > 0, \"Invalid proof\"); // TODO: replace by actual check\n require(!_received[id][_blockPeriod()], \"Proof already submitted\");\n _received[id][_blockPeriod()] = true;\n emit ProofSubmitted(id, proof);\n }\n\n function _markProofAsMissing(SlotId id, Period missedPeriod) internal {\n uint256 end = _periodEnd(missedPeriod);\n require(end < block.timestamp, \"Period has not ended yet\");\n require(block.timestamp < end + _config.timeout, \"Validation timed out\");\n require(!_received[id][missedPeriod], \"Proof was submitted, not missing\");\n require(_isProofRequired(id, missedPeriod), \"Proof was not required\");\n require(!_missing[id][missedPeriod], \"Proof already marked as missing\");\n _missing[id][missedPeriod] = true;\n _missed[id] += 1;\n }\n\n event ProofSubmitted(SlotId id, bytes proof);\n}\n"
|
|
},
|
|
"contracts/Requests.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\ntype RequestId is bytes32;\ntype SlotId is bytes32;\n\nstruct Request {\n address client;\n Ask ask;\n Content content;\n uint256 expiry; // timestamp as seconds since unix epoch at which this request expires\n bytes32 nonce; // random nonce to differentiate between similar requests\n}\n\nstruct Ask {\n uint64 slots; // the number of requested slots\n uint256 slotSize; // amount of storage per slot (in number of bytes)\n uint256 duration; // how long content should be stored (in seconds)\n uint256 proofProbability; // how often storage proofs are required\n uint256 reward; // amount of tokens paid per second per slot to hosts\n uint256 collateral; // amount of tokens required to be deposited by the hosts in order to fill the slot\n uint64 maxSlotLoss; // Max slots that can be lost without data considered to be lost\n}\n\nstruct Content {\n string cid; // content id (if part of a larger set, the chunk cid)\n Erasure erasure; // Erasure coding attributes\n PoR por; // Proof of Retrievability parameters\n}\n\nstruct Erasure {\n uint64 totalChunks; // the total number of chunks in the larger data set\n}\n\nstruct PoR {\n bytes u; // parameters u_1..u_s\n bytes publicKey; // public key\n bytes name; // random name\n}\n\nenum RequestState {\n New, // [default] waiting to fill slots\n Started, // all slots filled, accepting regular proofs\n Cancelled, // not enough slots filled before expiry\n Finished, // successfully completed\n Failed // too many nodes have failed to provide proofs, data lost\n}\n\nenum SlotState {\n Free, // [default] not filled yet, or host has vacated the slot\n Filled, // host has filled slot\n Finished, // successfully completed\n Failed, // the request has failed\n Paid // host has been paid\n}\n\nlibrary Requests {\n function id(Request memory request) internal pure returns (RequestId) {\n return RequestId.wrap(keccak256(abi.encode(request)));\n }\n\n function slotId(\n RequestId requestId,\n uint256 slotIndex\n ) internal pure returns (SlotId) {\n return SlotId.wrap(keccak256(abi.encode(requestId, slotIndex)));\n }\n\n function toRequestIds(\n bytes32[] memory ids\n ) internal pure returns (RequestId[] memory result) {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n result := ids\n }\n }\n\n function toSlotIds(\n bytes32[] memory ids\n ) internal pure returns (SlotId[] memory result) {\n // solhint-disable-next-line no-inline-assembly\n assembly {\n result := ids\n }\n }\n\n function pricePerSlot(\n Request memory request\n ) internal pure returns (uint256) {\n return request.ask.duration * request.ask.reward;\n }\n\n function price(Request memory request) internal pure returns (uint256) {\n return request.ask.slots * pricePerSlot(request);\n }\n}\n"
|
|
},
|
|
"contracts/StateRetrieval.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\nimport \"./Requests.sol\";\n\ncontract StateRetrieval {\n using EnumerableSet for EnumerableSet.Bytes32Set;\n using Requests for bytes32[];\n\n mapping(address => EnumerableSet.Bytes32Set) private _requestsPerClient;\n mapping(address => EnumerableSet.Bytes32Set) private _slotsPerHost;\n\n function myRequests() public view returns (RequestId[] memory) {\n return _requestsPerClient[msg.sender].values().toRequestIds();\n }\n\n function mySlots() public view returns (SlotId[] memory) {\n return _slotsPerHost[msg.sender].values().toSlotIds();\n }\n\n function _hasSlots(address host) internal view returns (bool) {\n return _slotsPerHost[host].length() > 0;\n }\n\n function _addToMyRequests(address client, RequestId requestId) internal {\n _requestsPerClient[client].add(RequestId.unwrap(requestId));\n }\n\n function _addToMySlots(address host, SlotId slotId) internal {\n _slotsPerHost[host].add(SlotId.unwrap(slotId));\n }\n\n function _removeFromMyRequests(address client, RequestId requestId) internal {\n _requestsPerClient[client].remove(RequestId.unwrap(requestId));\n }\n\n function _removeFromMySlots(address host, SlotId slotId) internal {\n _slotsPerHost[host].remove(SlotId.unwrap(slotId));\n }\n}\n"
|
|
},
|
|
"contracts/TestMarketplace.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./Marketplace.sol\";\n\n// exposes internal functions of Marketplace for testing\ncontract TestMarketplace is Marketplace {\n constructor(\n IERC20 token,\n MarketplaceConfig memory config\n )\n Marketplace(token, config) // solhint-disable-next-line no-empty-blocks\n {}\n\n function forciblyFreeSlot(SlotId slotId) public {\n _forciblyFreeSlot(slotId);\n }\n\n function getSlotCollateral(SlotId slotId) public view returns (uint256) {\n return _slots[slotId].currentCollateral;\n }\n}\n"
|
|
},
|
|
"contracts/TestProofs.sol": {
|
|
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./Proofs.sol\";\n\n// exposes internal functions of Proofs for testing\ncontract TestProofs is Proofs {\n mapping(SlotId => SlotState) private _states;\n\n // solhint-disable-next-line no-empty-blocks\n constructor(ProofConfig memory config) Proofs(config) {}\n\n function slotState(SlotId slotId) public view override returns (SlotState) {\n return _states[slotId];\n }\n\n function startRequiringProofs(SlotId slot, uint256 probability) public {\n _startRequiringProofs(slot, probability);\n }\n\n function markProofAsMissing(SlotId id, Period period) public {\n _markProofAsMissing(id, period);\n }\n\n function setSlotState(SlotId id, SlotState state) public {\n _states[id] = state;\n }\n}\n"
|
|
}
|
|
},
|
|
"settings": {
|
|
"optimizer": {
|
|
"enabled": true,
|
|
"runs": 1000
|
|
},
|
|
"outputSelection": {
|
|
"*": {
|
|
"*": [
|
|
"abi",
|
|
"evm.bytecode",
|
|
"evm.deployedBytecode",
|
|
"evm.methodIdentifiers",
|
|
"metadata",
|
|
"devdoc",
|
|
"userdoc",
|
|
"storageLayout",
|
|
"evm.gasEstimates"
|
|
],
|
|
"": [
|
|
"ast"
|
|
]
|
|
}
|
|
},
|
|
"metadata": {
|
|
"useLiteralContent": true
|
|
}
|
|
}
|
|
} |