Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Oase
- Optimization enabled
- true
- Compiler version
- v0.8.20+commit.a1b79de6
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2025-10-07T08:51:11.235441Z
Constructor Arguments
0x45711452762d9dab167c16cc1f8672d6b3102251471bbf07113c1e1134d39eea
Arg [0] (bytes32) : 45711452762d9dab167c16cc1f8672d6b3102251471bbf07113c1e1134d39eea
Contract source code
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0 ^0.8.20;
// npm/@openzeppelin/contracts@v4.9.6/token/ERC20/IERC20.sol
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// npm/@openzeppelin/contracts@v4.9.6/utils/Context.sol
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// npm/@openzeppelin/contracts@v5.0.0/utils/cryptography/ECDSA.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
// npm/@openzeppelin/contracts@v5.0.0/utils/cryptography/MerkleProof.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the Merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates Merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
*@dev The multiproof provided is not valid.
*/
error MerkleProofInvalidMultiproof();
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*/
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the Merkle tree.
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Sorts the pair (a, b) and hashes the result.
*/
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
// npm/@openzeppelin/contracts@v5.0.0/utils/math/Math.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
// npm/@openzeppelin/contracts@v5.0.0/utils/math/SignedMath.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
// npm/@openzeppelin/contracts@v4.9.6/token/ERC20/extensions/IERC20Metadata.sol
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// npm/@openzeppelin/contracts@v5.0.0/utils/Strings.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
// npm/@openzeppelin/contracts@v4.9.6/token/ERC20/ERC20.sol
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
// npm/@openzeppelin/contracts@v5.0.0/utils/cryptography/MessageHashUtils.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
// src/oase.sol
// All Rights Reserved
/**
* @title Oase
* @dev A savings token contract that provides a single saving/locking feature. The lock length is fixed at 369 days,
* and there is one global share rate that is recalculated daily.
*
* The daily share rate growth is determined by a formula that depends on the ratio of the
* current token value of all locked shares to the conceptual total supply (circulating supply
* plus the current token value of all locked shares).
* For example, if 100% of the conceptual total supply (by value) is locked, the target annual rate
* for share growth is approximately 3.69%. If only 10% (by value) is locked, it's approximately 36.9%,
* and if 1% (by value) is locked, it's approximately 369%, and so on.
*
* NOTE: All percentage references are annual. Daily growth is approximated by dividing the annual growth parameter by 365.
* This means the actual compounded Annual Percentage Rate (APR) is an approximation.
*/
contract Oase is ERC20 {
// ------------------------------------------------------------------------
// Global saving feature constants
// ------------------------------------------------------------------------
/**
* @dev Single, fixed lock length in days (369 days).
*/
uint256 public constant LOCK_LENGTH_DAYS = 369;
/**
* @dev Maximum number of days to process in a single _dailyDataUpdate call.
*/
uint256 private constant MAX_DAYS_TO_UPDATE = 369;
/**
* @dev Start of contract in Unix time. For demonstration, set to block.timestamp
* at deployment.
*/
uint256 public immutable LAUNCH_TIME;
// ------------------------------------------------------------------------
// Global saving system variables
// ------------------------------------------------------------------------
/**
* @dev Stores global variables for the contract's saving feature.
* It tracks the current shareRate, the number of days fully processed by dailyDataUpdate,
* the total amount of tokens initially deposited into active savings, and the total number of shares
* currently locked across all savings.
*/
struct Globals {
uint256 shareRate; // Current share rate (scaled by 1e8)
uint256 dailyDataCount; // Number of days fully processed by dailyDataUpdate
uint256 lockedTokenSupply; // Total tokens initially deposited and still locked
uint256 totalSharesLocked; // Total shares currently locked across all savings
}
/**
* @dev A copy of Globals in memory for calculations in each function call,
* to avoid repeated storage reads/writes.
*/
struct GlobalsCache {
uint256 _shareRate;
uint256 _dailyDataCount;
uint256 _lockedTokenSupply; // Total tokens initially deposited and still locked
uint256 _totalSharesLocked; // Total shares currently locked
uint256 _currentDay; // We also keep track of the "current day" in memory
}
Globals public globals; // The single instance of our Globals struct in storage
// ------------------------------------------------------------------------
// Structures / Storage for daily share rate data
// ------------------------------------------------------------------------
struct DailyDataStore {
uint256 dayShareRate; // records what the shareRate was after that day's update
}
mapping(uint256 => DailyDataStore) public dailyData;
// ------------------------------------------------------------------------
// Structures / Storage for each saving
// ------------------------------------------------------------------------
struct SavingStore {
uint40 savingId; // Unique ID for this saving
uint256 savedShares; // Number of shares purchased at start
uint256 savedTokens; // Number of tokens burned at start
uint256 savingStartDay; // Day the saving starts
uint256 savingEndDay; // Day the saving can be ended
}
/**
* @dev We keep a unique ID for each new saving across all addresses
*/
uint40 public latestSavingId;
/**
* @dev Each user can have multiple savings
*/
mapping(address => SavingStore[]) public savingsLists;
/**
* @dev Merkle root for verification of claim eligibility
*/
bytes32 public immutable merkleRoot;
/**
* @dev Tracks whether a claim ID has been used
*/
mapping(uint256 => bool) public hasClaimedId;
// ------------------------------------------------------------------------
// Events
// ------------------------------------------------------------------------
event DailyDataUpdate(
uint256 indexed timestamp,
uint256 indexed currentDay,
uint256 newShareRate
);
event SavingStart(
uint256 indexed data, // Packed data of (block.timestamp, savedTokens, savedShares)
address indexed saverAddr,
uint40 indexed savingId
);
event SavingEnd(
uint256 indexed data, // Packed data of (block.timestamp, savedShares, payoutTokens)
address indexed saverAddr,
uint40 indexed savingId
);
/**
* @dev Emitted when a claim is successfully processed
*/
event Claimed(
uint256 indexed id,
address indexed account,
uint256 amount,
uint256 timestamp
);
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
constructor(bytes32 _merkleRoot)
ERC20("Sway", "SWAY") // Name and Symbol via OpenZeppelin's constructor
{
merkleRoot = _merkleRoot;
LAUNCH_TIME = block.timestamp;
// Initialize our persistent storage
globals.shareRate = 1e8; // Start shareRate at 1.0 (scaled by 1e8)
globals.dailyDataCount = 0;
globals.lockedTokenSupply = 0; // Initialize total tokens locked
globals.totalSharesLocked = 0; // Initialize new total shares locked
}
// ------------------------------------------------------------------------
// ERC20 Overrides
// ------------------------------------------------------------------------
/**
* @dev Returns the number of decimals used to get its user representation.
* Overrides the default 18 decimals to use 8 decimals.
*/
function decimals() public view virtual override returns (uint8) {
return 8;
}
// ------------------------------------------------------------------------
// GlobalsCache load/sync
// ------------------------------------------------------------------------
/**
* @dev Load global values into a memory-based cache so we can manipulate them
* without constantly reading from storage.
*/
function _globalsLoad(GlobalsCache memory g, GlobalsCache memory gSnapshot)
internal
view
{
g._shareRate = globals.shareRate;
g._dailyDataCount = globals.dailyDataCount;
g._lockedTokenSupply = globals.lockedTokenSupply; // Load total tokens locked
g._totalSharesLocked = globals.totalSharesLocked; // Load total shares locked
g._currentDay = _currentDay();
_globalsCacheSnapshot(g, gSnapshot);
}
/**
* @dev Take a snapshot of the current memory-based GlobalsCache.
* We'll compare with this snapshot to see if anything changed
* before writing back to storage.
*/
function _globalsCacheSnapshot(GlobalsCache memory g, GlobalsCache memory gSnapshot)
private
pure
{
gSnapshot._shareRate = g._shareRate;
gSnapshot._dailyDataCount = g._dailyDataCount;
gSnapshot._lockedTokenSupply = g._lockedTokenSupply; // Snapshot total tokens locked
gSnapshot._totalSharesLocked = g._totalSharesLocked; // Snapshot total shares locked
gSnapshot._currentDay = g._currentDay;
}
/**
* @dev Compare memory-based GlobalsCache to the snapshot. If anything changed,
* write the updated values back to storage. This saves gas by storing only
* if changes occurred.
*/
function _globalsSync(GlobalsCache memory g, GlobalsCache memory gSnapshot)
internal
{
// If any relevant field changed, update
if (
g._shareRate != gSnapshot._shareRate
|| g._dailyDataCount != gSnapshot._dailyDataCount
|| g._lockedTokenSupply != gSnapshot._lockedTokenSupply // Check total tokens locked
|| g._totalSharesLocked != gSnapshot._totalSharesLocked // Check total shares locked
) {
globals.shareRate = g._shareRate;
globals.dailyDataCount = g._dailyDataCount;
globals.lockedTokenSupply = g._lockedTokenSupply; // Sync total tokens locked
globals.totalSharesLocked = g._totalSharesLocked; // Sync total shares locked
}
}
// ------------------------------------------------------------------------
// Time / Day Utilities
// ------------------------------------------------------------------------
function _currentDay() internal view returns (uint256) {
// Each "day" is a 24-hour period from LAUNCH_TIME
return (block.timestamp - LAUNCH_TIME) / 1 days;
}
// ------------------------------------------------------------------------
// Internal daily data update logic
// ------------------------------------------------------------------------
/**
* @dev Processes daily updates to the share rate. It iterates day-by-day from the last fully processed day
* (`g._dailyDataCount`) up to (but not including) the target `beforeDay`. In each iteration for a `currentDay`,
* it first records the existing `g._shareRate` (representing the rate at the start of `currentDay + 1`)
* into `dailyData[currentDay + 1]`. Then, it calculates and applies the share rate growth for `currentDay`
* to `g._shareRate`. The loop runs for a maximum of MAX_DAYS_TO_UPDATE days per call.
* @param g The GlobalsCache memory struct, containing current global values.
* @param beforeDay The target day to process updates up to (exclusive).
* @return completed True if the update process reached `beforeDay`, false otherwise (e.g., if MAX_DAYS_TO_UPDATE limit was hit).
*/
function _dailyDataUpdate(GlobalsCache memory g, uint256 beforeDay) private returns (bool completed) {
uint256 daysToProcess = beforeDay > g._dailyDataCount ? beforeDay - g._dailyDataCount : 0;
if (daysToProcess == 0) {
return true; // Already up-to-date
}
uint256 daysThisRun = daysToProcess > MAX_DAYS_TO_UPDATE ? MAX_DAYS_TO_UPDATE : daysToProcess;
uint256 endDayForLoop = g._dailyDataCount + daysThisRun;
// Process each day from dailyDataCount up to endDayForLoop
for (uint256 currentDay = g._dailyDataCount; currentDay < endDayForLoop; currentDay++) {
// 1) Record the current shareRate in dailyData
dailyData[currentDay + 1] = DailyDataStore({
dayShareRate: g._shareRate
});
// 2) Apply next day's rate adjustments
_applyDailyRate(g);
}
// Update dailyDataCount to the day we processed up to
g._dailyDataCount = endDayForLoop;
// Emit a single event showing the final shareRate and day processed up to
// Note: The event timestamp is current block, but day processed might be in the past
emit DailyDataUpdate(block.timestamp, g._dailyDataCount, g._shareRate);
// Return true only if we reached the target day
return g._dailyDataCount == beforeDay;
}
/**
* @dev Calculates and applies the share rate growth for a single day. The growth is determined by
* a ratio derived from the current token value of all locked shares relative to the conceptual total supply.
*/
function _applyDailyRate(GlobalsCache memory g) internal view {
// Get locked ratio in 1e8 scale
uint256 ratio = _getLockedRatio(g);
// If ratio is 0 (returned by _getLockedRatio if currentValueOfLockedShares is 0),
// the share rate should not increase. It remains unchanged for this day.
if (ratio == 0) {
return; // No change to g._shareRate
}
// If ratio is non-zero, _getLockedRatio ensures it's at least 1e5 (0.1%).
// Calculate target annual APR (in 1e8 scale)
// 3.69% when ratio is 100%, 36.9% when ratio is 10%, 369% when ratio is 1%.
// Min ratio of 1e5 (0.1%) means max APR from this formula is 36900%.
uint256 targetAnnualPercent = (3690000 * 1e8) / ratio;
// Calculate daily rate that will compound to the annual multiplier
// Formula: dailyRate = (annualMultiplier)^(1/365) - 1
// Note on Approximations, Achieved APY for Savers, and Total Supply Inflation:
//
// 1. Approximation Precision and Achieved APY for Savers:
// The daily rate calculation uses approximations. These may become less precise at very
// high targetAnnualPercent values (which occur when the locked token ratio is low).
// In such scenarios, the approximation tends to be conservative (i.e., it underestimates
// the required daily rate), potentially resulting in an actual compounded APY for savers
// that is lower than the high targetAnnualPercent. This means that while a lower locked
// ratio aims for a higher *targeted APY for savers*, the actually achieved APY might be
// tempered by this approximation. Conversely, for lower targetAnnualPercent values
// (e.g., at high locked ratios), the deviation between the actual APY for savers and
// the targetAnnualPercent might differ (often being slightly higher due to the nature of the approximation).
//
// 2. Impact on Total Supply Inflation:
// This interplay affects the overall total supply inflation (i.e., the total number of new tokens minted).
// Extremely low locked ratios (e.g., <1%) target very high APYs for savers. However, the
// actually achieved APY on this small base of locked tokens (potentially reduced further by the
// approximation's conservatism at high targets) can result in fewer absolute new tokens minted
// compared to scenarios with higher locked ratios.
// Higher locked ratios (e.g., 10%-100%) apply a more moderate (and more accurately achieved) APY
// for savers to a larger base of locked tokens. Consequently, the total supply inflation
// (measured as new tokens minted as a percentage of the conceptual total supply) may actually
// be higher when more tokens are locked. This total supply inflation might not significantly
// exceed the ~3.69% annual rate (which is achieved when 100% of tokens are locked and earn
// a ~3.69% APY for savers), even if APY targets for savers are drastically higher at very
// low locked ratios.
// Conservative approximation that works for our range:
uint256 dailyRate;
if (targetAnnualPercent <= 10e8) { // <= 1000%
// Use logarithmic approximation: ln(1+x) ≈ x/(1+x/2)
dailyRate = (targetAnnualPercent * 1e8) / (1e8 + targetAnnualPercent / 2) / 365;
} else {
// For very high rates, use direct calculation with better approximation
// This gives a more accurate daily rate for high annual percentages
dailyRate = (targetAnnualPercent * 1e8) / (1e8 + targetAnnualPercent / 3) / 365;
}
// Apply the daily rate to the share rate
g._shareRate = (g._shareRate * (1e8 + dailyRate)) / 1e8;
}
// ------------------------------------------------------------------------
// Ratio / Rate Calculation Helpers
// ------------------------------------------------------------------------
/**
* @dev Calculates the ratio of the current token value of all locked shares
* to the conceptual total supply (circulating supply + current token value of locked shares).
* The ratio is scaled by 1e8 (e.g., 1e8 represents a 1.0 ratio, 1e7 represents 0.1).
*/
function _getLockedRatio(GlobalsCache memory g) internal view returns (uint256) {
// Use totalSupply() from ERC20 (which is circulating supply)
uint256 circulatingSupply = totalSupply();
// Calculate the current "value" of all locked shares in terms of tokens
uint256 currentValueOfLockedShares;
if (g._totalSharesLocked == 0 || g._shareRate == 0) { // Or if shareRate is 0 to prevent division by zero issues with shares.
currentValueOfLockedShares = 0;
} else {
// Note: _shareRate is scaled by 1e8. _totalSharesLocked is the sum of shares.
// If shareRate was 1e8 initially, shares = tokens.
// So, value = (shares * currentShareRate) / 1e8 is correct.
currentValueOfLockedShares = (g._totalSharesLocked * g._shareRate) / 1e8;
}
uint256 definedConceptualTotal = circulatingSupply + currentValueOfLockedShares;
if (definedConceptualTotal == 0) {
// If the conceptual total supply is zero (no circulating supply and no value in locked shares), the ratio is 0.
// This results in no share rate increase for the day when processed by _applyDailyRate.
return 0;
}
if (currentValueOfLockedShares == 0) {
// If there's a conceptual total supply but no value is currently locked (e.g., all shares have zero value or no shares exist), the ratio is 0.
// This also results in no share rate increase for the day via _applyDailyRate.
return 0;
}
// Calculate ratio: (currentValueOfLockedShares * 1e8) / definedConceptualTotal
uint256 ratio = (currentValueOfLockedShares * 1e8) / definedConceptualTotal;
// Enforce minimum ratio of 0.1% to prevent excessive share rate growth if actual ratio is extremely low.
// 1e5 in 1e8 scale equals 0.1%.
if (ratio < 1e5) {
return 1e5;
}
return ratio;
}
// ------------------------------------------------------------------------
// Saving Feature (Single 369-day lock)
// ------------------------------------------------------------------------
/**
* @dev Start a new saving by burning the specified number of tokens.
* The user receives "shares" at the current shareRate.
* The saving can only be ended after the fixed 369-day period.
*/
function saveStart(uint256 newSavedTokens) external {
require(newSavedTokens > 0, "Oase: Must save more than 0");
require(newSavedTokens >= 1 * 10**8, "Oase: Minimum save is 1 token");
require(newSavedTokens < 2**72, "Oase: Amount too large for event packing");
// use balanceOf from ERC20
require(balanceOf(msg.sender) >= newSavedTokens, "Oase: insufficient balance");
require(savingsLists[msg.sender].length < 100, "Oase: User has reached the maximum number of savings");
// Load globals and take snapshot
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
// Update daily data, require it to be complete
bool updated = _dailyDataUpdate(g, _currentDay());
require(updated, "Oase: Daily data update incomplete, call updateDailyData()");
// Increment saving ID
uint40 newSavingId = ++latestSavingId;
// Calculate shares at the current shareRate FIRST
// shares = tokens * 1e8 / shareRate
uint256 newSavingShares = (newSavedTokens * 1e8) / g._shareRate;
require(newSavingShares > 0, "Oase: Amount too small for current share rate");
require(newSavingShares < 2**72, "Oase: Shares too large for event packing");
// Now that we know the shares are valid, burn the tokens
// Use inherited _burn instead of custom storage
_burn(msg.sender, newSavedTokens);
// savingStartDay is next day
uint256 newSavingStartDay = g._currentDay + 1;
// savingEndDay is + 369 from start
uint256 newSavingEndDay = newSavingStartDay + LOCK_LENGTH_DAYS;
// Create new saving entry
savingsLists[msg.sender].push(SavingStore({
savingId: newSavingId,
savedShares: newSavingShares,
savedTokens: newSavedTokens,
savingStartDay: newSavingStartDay,
savingEndDay: newSavingEndDay
}));
// Increase locked supply (original tokens) and total shares locked
g._lockedTokenSupply += newSavedTokens; // Track initial tokens locked
g._totalSharesLocked += newSavingShares; // Track total shares
// Log
emit SavingStart(
uint256(uint40(block.timestamp))
| (uint256(uint72(newSavedTokens)) << 40)
| (uint256(uint72(newSavingShares)) << 112),
msg.sender,
newSavingId
);
// Sync changes back to storage
_globalsSync(g, gSnapshot);
}
/**
* @dev End a saving that has completed its 369-day lock.
* We compute the final payout by converting shares to tokens at the *current* shareRate.
* We also auto-update daily if needed.
*/
function saveEnd(uint256 saveIndex, uint40 saveIdParam) external {
// Load globals and take snapshot
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
// Update daily data, require it to be complete
uint256 currentDay = _currentDay();
bool updated = _dailyDataUpdate(g, currentDay);
require(updated, "Oase: Daily data update incomplete, call updateDailyData()");
SavingStore[] storage userSavings = savingsLists[msg.sender];
require(userSavings.length != 0, "Oase: no savings");
require(saveIndex < userSavings.length, "Oase: invalid index");
SavingStore storage stRef = userSavings[saveIndex];
require(stRef.savingId == saveIdParam, "Oase: savingId mismatch");
// Ensure 369 days have passed from savingEndDay
require(g._currentDay >= stRef.savingEndDay, "Oase: saving not matured");
// Calculate payout
// payout = shares * shareRate / 1e8
uint256 payoutTokens = (stRef.savedShares * g._shareRate) / 1e8;
// Emit the event now, including the number of shares & final payout
emit SavingEnd(
uint256(uint40(block.timestamp))
| (uint256(uint72(stRef.savedShares)) << 40)
| (uint256(uint72(payoutTokens)) << 112),
msg.sender,
stRef.savingId
);
// Remove from locked supply
g._lockedTokenSupply -= stRef.savedTokens;
g._totalSharesLocked -= stRef.savedShares;
// Remove saving from array by swapping with last & popping
_removeSaving(userSavings, saveIndex);
// Sync
_globalsSync(g, gSnapshot);
// Mint final tokens to user
_mint(msg.sender, payoutTokens);
}
// ------------------------------------------------------------------------
// Internal Helpers
// ------------------------------------------------------------------------
/**
* @dev Public function to allow anyone to advance the daily data updates.
* Processes up to MAX_DAYS_TO_UPDATE days per call.
* Call this multiple times if needed to catch up a large backlog.
*/
function updateDailyData() external {
GlobalsCache memory g;
GlobalsCache memory gSnapshot;
_globalsLoad(g, gSnapshot);
// Call _dailyDataUpdate but ignore the return value.
// We just want to process a batch if there's a backlog.
_dailyDataUpdate(g, _currentDay());
_globalsSync(g, gSnapshot);
}
/**
* @dev Remove an element from an array by swapping with the last and popping.
*/
function _removeSaving(SavingStore[] storage arr, uint256 index) internal {
uint256 lastIndex = arr.length - 1;
if (index != lastIndex) {
arr[index] = arr[lastIndex];
}
arr.pop();
}
/**
* @dev Returns all of a user's SavingStore entries in a single array.
*/
function getUserSavings(address user) external view returns (SavingStore[] memory) {
return savingsLists[user];
}
/**
* @dev Allows users to claim tokens if they are in the merkle tree.
* @param id Unique identifier for the claim
* @param amount Amount of tokens to claim
* @param mintingStartDate Start date of the claim period
* @param mintingEndDate End date of the claim period
* @param merkleProof Merkle proof verifying the claim
* @param signature Optional signature if claiming on behalf of someone else
*/
function claim(
uint256 id,
uint256 amount,
uint256 mintingStartDate,
uint256 mintingEndDate,
bytes32[] calldata merkleProof,
bytes calldata signature
) external {
require(block.timestamp >= mintingStartDate && block.timestamp <= mintingEndDate, "Not within the minting window.");
require(!hasClaimedId[id], "This ID has already been claimed.");
address eligibleAddress;
if (signature.length > 0) {
// Case with signature: Recover the eligible address from the signature
// Include all relevant parameters to prevent replay attacks across contracts, chains, and time windows
bytes32 messageHash = keccak256(abi.encode(
msg.sender,
id,
amount,
mintingStartDate,
mintingEndDate,
block.chainid,
address(this)
));
bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(messageHash);
eligibleAddress = ECDSA.recover(ethSignedMessageHash, signature);
} else {
eligibleAddress = msg.sender;
}
// Construct the node from the eligible address
bytes32 node = keccak256(abi.encode(id, eligibleAddress, amount, mintingStartDate, mintingEndDate));
// Verify the provided Merkle proof against the stored Merkle root
require(MerkleProof.verify(merkleProof, merkleRoot, node), "Invalid Merkle proof.");
// Record the claim and mint the tokens
hasClaimedId[id] = true;
_mint(msg.sender, amount);
emit Claimed(id, msg.sender, amount, block.timestamp);
}
}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"bytes32","name":"_merkleRoot","internalType":"bytes32"}]},{"type":"error","name":"ECDSAInvalidSignature","inputs":[]},{"type":"error","name":"ECDSAInvalidSignatureLength","inputs":[{"type":"uint256","name":"length","internalType":"uint256"}]},{"type":"error","name":"ECDSAInvalidSignatureS","inputs":[{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Claimed","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DailyDataUpdate","inputs":[{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":true},{"type":"uint256","name":"currentDay","internalType":"uint256","indexed":true},{"type":"uint256","name":"newShareRate","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SavingEnd","inputs":[{"type":"uint256","name":"data","internalType":"uint256","indexed":true},{"type":"address","name":"saverAddr","internalType":"address","indexed":true},{"type":"uint40","name":"savingId","internalType":"uint40","indexed":true}],"anonymous":false},{"type":"event","name":"SavingStart","inputs":[{"type":"uint256","name":"data","internalType":"uint256","indexed":true},{"type":"address","name":"saverAddr","internalType":"address","indexed":true},{"type":"uint40","name":"savingId","internalType":"uint40","indexed":true}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"LAUNCH_TIME","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"LOCK_LENGTH_DAYS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"mintingStartDate","internalType":"uint256"},{"type":"uint256","name":"mintingEndDate","internalType":"uint256"},{"type":"bytes32[]","name":"merkleProof","internalType":"bytes32[]"},{"type":"bytes","name":"signature","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"dayShareRate","internalType":"uint256"}],"name":"dailyData","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"","internalType":"struct Oase.SavingStore[]","components":[{"type":"uint40","name":"savingId","internalType":"uint40"},{"type":"uint256","name":"savedShares","internalType":"uint256"},{"type":"uint256","name":"savedTokens","internalType":"uint256"},{"type":"uint256","name":"savingStartDay","internalType":"uint256"},{"type":"uint256","name":"savingEndDay","internalType":"uint256"}]}],"name":"getUserSavings","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"shareRate","internalType":"uint256"},{"type":"uint256","name":"dailyDataCount","internalType":"uint256"},{"type":"uint256","name":"lockedTokenSupply","internalType":"uint256"},{"type":"uint256","name":"totalSharesLocked","internalType":"uint256"}],"name":"globals","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasClaimedId","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint40","name":"","internalType":"uint40"}],"name":"latestSavingId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"merkleRoot","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"saveEnd","inputs":[{"type":"uint256","name":"saveIndex","internalType":"uint256"},{"type":"uint40","name":"saveIdParam","internalType":"uint40"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"saveStart","inputs":[{"type":"uint256","name":"newSavedTokens","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint40","name":"savingId","internalType":"uint40"},{"type":"uint256","name":"savedShares","internalType":"uint256"},{"type":"uint256","name":"savedTokens","internalType":"uint256"},{"type":"uint256","name":"savingStartDay","internalType":"uint256"},{"type":"uint256","name":"savingEndDay","internalType":"uint256"}],"name":"savingsLists","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateDailyData","inputs":[]}]
Contract Creation Code
0x60c060405234801562000010575f80fd5b5060405162002493380380620024938339810160408190526200003391620000b3565b604051806040016040528060048152602001635377617960e01b815250604051806040016040528060048152602001635357415960e01b81525081600390816200007e91906200016b565b5060046200008d82826200016b565b50505060a052426080526305f5e1006005555f6006819055600781905560085562000233565b5f60208284031215620000c4575f80fd5b5051919050565b634e487b7160e01b5f52604160045260245ffd5b600181811c90821680620000f457607f821691505b6020821081036200011357634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111562000166575f81815260208120601f850160051c81016020861015620001415750805b601f850160051c820191505b8181101562000162578281556001016200014d565b5050505b505050565b81516001600160401b03811115620001875762000187620000cb565b6200019f81620001988454620000df565b8462000119565b602080601f831160018114620001d5575f8415620001bd5750858301515b5f19600386901b1c1916600185901b17855562000162565b5f85815260208120601f198616915b828110156200020557888601518255948401946001909101908401620001e4565b50858210156200022357878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a051612230620002635f395f81816101e901526106f501525f818161015c01526115e601526122305ff3fe608060405234801561000f575f80fd5b5060043610610153575f3560e01c806390de6871116100bf578063db54576f11610079578063db54576f14610335578063dd62ed3e1461033d578063dfd8bf6814610350578063f80a205714610359578063f8f8b5a314610379578063fa0ff5a0146103bc575f80fd5b806390de68711461029057806395d89b41146102af578063a457c2d7146102b7578063a9059cbb146102ca578063c3124525146102dd578063cfd049d514610313575f80fd5b8063313ce56711610110578063313ce5671461020b578063395093511461021a5780635bb58d661461022d578063662a025e1461024257806370a08231146102555780638b4729d71461027d575f80fd5b8063022466b51461015757806306fdde0314610191578063095ea7b3146101a657806318160ddd146101c957806323b872dd146101d15780632eb4a7ab146101e4575b5f80fd5b61017e7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6101996103e3565b6040516101889190611da9565b6101b96101b4366004611e0f565b610473565b6040519015158152602001610188565b60025461017e565b6101b96101df366004611e37565b61048c565b61017e7f000000000000000000000000000000000000000000000000000000000000000081565b60405160088152602001610188565b6101b9610228366004611e0f565b6104af565b61024061023b366004611eb5565b6104d0565b005b610240610250366004611f72565b6107ce565b61017e610263366004611f89565b6001600160a01b03165f9081526020819052604090205490565b61024061028b366004611fa2565b610cb2565b61017e61029e366004611f72565b60096020525f908152604090205481565b610199610f5f565b6101b96102c5366004611e0f565b610f6e565b6101b96102d8366004611e0f565b610fe8565b6005546006546007546008546102f39392919084565b604080519485526020850193909352918301526060820152608001610188565b6101b9610321366004611f72565b600c6020525f908152604090205460ff1681565b610240610ff5565b61017e61034b366004611fd9565b61102a565b61017e61017181565b61036c610367366004611f89565b611054565b604051610188919061200a565b61038c610387366004611e0f565b6110fa565b6040805164ffffffffff90961686526020860194909452928401919091526060830152608082015260a001610188565b600a546103cd9064ffffffffff1681565b60405164ffffffffff9091168152602001610188565b6060600380546103f29061207e565b80601f016020809104026020016040519081016040528092919081815260200182805461041e9061207e565b80156104695780601f1061044057610100808354040283529160200191610469565b820191905f5260205f20905b81548152906001019060200180831161044c57829003601f168201915b5050505050905090565b5f3361048081858561114c565b60019150505b92915050565b5f33610499858285611270565b6104a48585856112e8565b506001949350505050565b5f336104808185856104c1838361102a565b6104cb91906120ca565b61114c565b8542101580156104e05750844211155b6105315760405162461bcd60e51b815260206004820152601e60248201527f4e6f742077697468696e20746865206d696e74696e672077696e646f772e000060448201526064015b60405180910390fd5b5f888152600c602052604090205460ff16156105995760405162461bcd60e51b815260206004820152602160248201527f546869732049442068617320616c7265616479206265656e20636c61696d65646044820152601760f91b6064820152608401610528565b5f811561067057604080513360208201529081018a9052606081018990526080810188905260a081018790524660c08201523060e08201525f90610100016040516020818303038152906040528051906020012090505f610626827f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c91909152603c902090565b90506106678186868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061148a92505050565b92505050610673565b50335b60408051602081018b90526001600160a01b03831691810191909152606081018990526080810188905260a081018790525f9060c0016040516020818303038152906040528051906020012090506107208686808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152507f000000000000000000000000000000000000000000000000000000000000000092508591506114b29050565b6107645760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b21026b2b935b63290383937b7b31760591b6044820152606401610528565b5f8a8152600c60205260409020805460ff19166001179055610786338a6114c7565b604080518a815242602082015233918c917fd9cb1e2714d65a111c0f20f060176ad657496bd47a3de04ec7c3d4ca232112ac910160405180910390a350505050505050505050565b5f811161081d5760405162461bcd60e51b815260206004820152601b60248201527f4f6173653a204d7573742073617665206d6f7265207468616e203000000000006044820152606401610528565b6305f5e1008110156108715760405162461bcd60e51b815260206004820152601d60248201527f4f6173653a204d696e696d756d2073617665206973203120746f6b656e0000006044820152606401610528565b600160481b81106108d55760405162461bcd60e51b815260206004820152602860248201527f4f6173653a20416d6f756e7420746f6f206c6172676520666f72206576656e74604482015267207061636b696e6760c01b6064820152608401610528565b335f908152602081905260409020548111156109335760405162461bcd60e51b815260206004820152601a60248201527f4f6173653a20696e73756666696369656e742062616c616e63650000000000006044820152606401610528565b335f908152600b60205260409020546064116109ae5760405162461bcd60e51b815260206004820152603460248201527f4f6173653a205573657220686173207265616368656420746865206d6178696d604482015273756d206e756d626572206f6620736176696e677360601b6064820152608401610528565b6109b6611d7f565b6109be611d7f565b6109c88282611584565b5f6109da836109d56115dc565b61161a565b9050806109f95760405162461bcd60e51b8152600401610528906120dd565b600a80545f91908290610a129064ffffffffff1661213a565b91906101000a81548164ffffffffff021916908364ffffffffff160217905590505f845f0151866305f5e100610a48919061215d565b610a529190612174565b90505f8111610ab95760405162461bcd60e51b815260206004820152602d60248201527f4f6173653a20416d6f756e7420746f6f20736d616c6c20666f7220637572726560448201526c6e74207368617265207261746560981b6064820152608401610528565b600160481b8110610b1d5760405162461bcd60e51b815260206004820152602860248201527f4f6173653a2053686172657320746f6f206c6172676520666f72206576656e74604482015267207061636b696e6760c01b6064820152608401610528565b610b273387611721565b5f85608001516001610b3991906120ca565b90505f610b48610171836120ca565b9050600b5f336001600160a01b03166001600160a01b031681526020019081526020015f206040518060a001604052808664ffffffffff1681526020018581526020018a815260200184815260200183815250908060018154018082558091505060019003905f5260205f2090600502015f909190919091505f820151815f015f6101000a81548164ffffffffff021916908364ffffffffff1602179055506020820151816001015560408201518160020155606082015181600301556080820151816004015550508787604001818151610c2391906120ca565b905250606087018051849190610c3a9083906120ca565b90525060405164ffffffffff80861691339142166dffffffffffffffffff000000000060288d901b161768ffffffffffffffffff60701b607088901b1617907f390cc673cefaad1ca89a4ddf40d5a3199e7ffab1ad3d1c408fa22e7e473102a7905f90a4610ca88787611849565b5050505050505050565b610cba611d7f565b610cc2611d7f565b610ccc8282611584565b5f610cd56115dc565b90505f610ce2848361161a565b905080610d015760405162461bcd60e51b8152600401610528906120dd565b335f908152600b602052604081208054909103610d535760405162461bcd60e51b815260206004820152601060248201526f4f6173653a206e6f20736176696e677360801b6044820152606401610528565b80548710610d995760405162461bcd60e51b815260206004820152601360248201527209ec2e6ca7440d2dcecc2d8d2c840d2dcc8caf606b1b6044820152606401610528565b5f818881548110610dac57610dac612193565b5f9182526020909120600590910201805490915064ffffffffff888116911614610e185760405162461bcd60e51b815260206004820152601760248201527f4f6173653a20736176696e674964206d69736d617463680000000000000000006044820152606401610528565b806004015486608001511015610e705760405162461bcd60e51b815260206004820152601860248201527f4f6173653a20736176696e67206e6f74206d61747572656400000000000000006044820152606401610528565b5f6305f5e100875f01518360010154610e89919061215d565b610e939190612174565b8254600184015460405192935064ffffffffff918216923392607086901b68ffffffffffffffffff60701b1660289390931b6dffffffffffffffffff000000000016429091161791909117907ff0019f5bb966673bc3d326e7d459457783cb80b7edb41cf11164fbc23e9d2f93905f90a4816002015487604001818151610f1a91906121a7565b9052506001820154606088018051610f339083906121a7565b905250610f40838a6118ac565b610f4a8787611849565b610f5433826114c7565b505050505050505050565b6060600480546103f29061207e565b5f3381610f7b828661102a565b905083811015610fdb5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610528565b6104a4828686840361114c565b5f336104808185856112e8565b610ffd611d7f565b611005611d7f565b61100f8282611584565b61101b826109d56115dc565b506110268282611849565b5050565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0381165f908152600b60209081526040808320805482518185028101850190935280835260609492939192909184015b828210156110ef575f8481526020908190206040805160a08101825260058602909201805464ffffffffff1683526001808201548486015260028201549284019290925260038101546060840152600401546080830152908352909201910161108b565b505050509050919050565b600b602052815f5260405f208181548110611113575f80fd5b5f9182526020909120600590910201805460018201546002830154600384015460049094015464ffffffffff9093169550909350919085565b6001600160a01b0383166111ae5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610528565b6001600160a01b03821661120f5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610528565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f61127b848461102a565b90505f1981146112e257818110156112d55760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610528565b6112e2848484840361114c565b50505050565b6001600160a01b03831661134c5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610528565b6001600160a01b0382166113ae5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610528565b6001600160a01b0383165f90815260208190526040902054818110156114255760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610528565b6001600160a01b038481165f81815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36112e2565b5f805f806114988686611996565b9250925092506114a882826119df565b5090949350505050565b5f826114be8584611a97565b14949350505050565b6001600160a01b03821661151d5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610528565b8060025f82825461152e91906120ca565b90915550506001600160a01b0382165f81815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60055482526006546020830152600754604083015260085460608301526115a96115dc565b60808381019182528351835260208085015190840152604080850151908401526060938401519383019390935251910152565b5f6201518061160b7f0000000000000000000000000000000000000000000000000000000000000000426121a7565b6116159190612174565b905090565b5f808360200151831161162d575f61163c565b602084015161163c90846121a7565b9050805f0361164f576001915050610486565b5f610171821161165f5781611663565b6101715b90505f81866020015161167691906120ca565b60208701519091505b818110156116d25760408051602081019091528751815260095f6116a48460016120ca565b815260208101919091526040015f20905190556116c087611ae3565b806116ca816121ba565b91505061167f565b5060208681018290528651604051908152829142917f8caa10406f5fa75d56a3b35f15b317829cd60a331e7048de76e042d088203bfb910160405180910390a350505060209092015114919050565b6001600160a01b0382166117815760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610528565b6001600160a01b0382165f90815260208190526040902054818110156117f45760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610528565b6001600160a01b0383165f818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101611263565b8051825114158061186257508060200151826020015114155b8061187557508060400151826040015114155b8061188857508060600151826060015114155b15611026575080516005556020810151600655604081015160075560600151600855565b81545f906118bc906001906121a7565b9050808214611947578281815481106118d7576118d7612193565b905f5260205f2090600502018383815481106118f5576118f5612193565b5f91825260209091208254600590920201805464ffffffffff191664ffffffffff9092169190911781556001808301549082015560028083015490820155600380830154908201556004918201549101555b82805480611957576119576121d2565b5f8281526020812060055f1990930192830201805464ffffffffff19168155600181018290556002810182905560038101829055600401559055505050565b5f805f83516041036119cd576020840151604085015160608601515f1a6119bf88828585611bcd565b9550955095505050506119d8565b505081515f91506002905b9250925092565b5f8260038111156119f2576119f26121e6565b036119fb575050565b6001826003811115611a0f57611a0f6121e6565b03611a2d5760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611a4157611a416121e6565b03611a625760405163fce698f760e01b815260048101829052602401610528565b6003826003811115611a7657611a766121e6565b03611026576040516335e2f38360e21b815260048101829052602401610528565b5f81815b8451811015611adb57611ac782868381518110611aba57611aba612193565b6020026020010151611c95565b915080611ad3816121ba565b915050611a9b565b509392505050565b5f611aed82611cc4565b9050805f03611afa575050565b5f611b0c8266014f9a81ec1000612174565b90505f633b9aca008211611b5f5761016d611b28600284612174565b611b36906305f5e1006120ca565b611b44846305f5e10061215d565b611b4e9190612174565b611b589190612174565b9050611ba0565b61016d611b6d600384612174565b611b7b906305f5e1006120ca565b611b89846305f5e10061215d565b611b939190612174565b611b9d9190612174565b90505b6305f5e100611baf82826120ca565b8551611bbb919061215d565b611bc59190612174565b909352505050565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611c0657505f91506003905082611c8b565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611c57573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116611c8257505f925060019150829050611c8b565b92505f91508190505b9450945094915050565b5f818310611caf575f828152602084905260409020611cbd565b5f8381526020839052604090205b9392505050565b5f80611ccf60025490565b90505f83606001515f1480611ce357508351155b15611cef57505f611d12565b835160608501516305f5e10091611d059161215d565b611d0f9190612174565b90505b5f611d1d82846120ca565b9050805f03611d3057505f949350505050565b815f03611d4157505f949350505050565b5f81611d51846305f5e10061215d565b611d5b9190612174565b9050620186a0811015611d765750620186a095945050505050565b95945050505050565b6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b5f6020808352835180828501525f5b81811015611dd457858101830151858201604001528201611db8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114611e0a575f80fd5b919050565b5f8060408385031215611e20575f80fd5b611e2983611df4565b946020939093013593505050565b5f805f60608486031215611e49575f80fd5b611e5284611df4565b9250611e6060208501611df4565b9150604084013590509250925092565b5f8083601f840112611e80575f80fd5b50813567ffffffffffffffff811115611e97575f80fd5b602083019150836020828501011115611eae575f80fd5b9250929050565b5f805f805f805f8060c0898b031215611ecc575f80fd5b88359750602089013596506040890135955060608901359450608089013567ffffffffffffffff80821115611eff575f80fd5b818b0191508b601f830112611f12575f80fd5b813581811115611f20575f80fd5b8c60208260051b8501011115611f34575f80fd5b6020830196508095505060a08b0135915080821115611f51575f80fd5b50611f5e8b828c01611e70565b999c989b5096995094979396929594505050565b5f60208284031215611f82575f80fd5b5035919050565b5f60208284031215611f99575f80fd5b611cbd82611df4565b5f8060408385031215611fb3575f80fd5b82359150602083013564ffffffffff81168114611fce575f80fd5b809150509250929050565b5f8060408385031215611fea575f80fd5b611ff383611df4565b915061200160208401611df4565b90509250929050565b602080825282518282018190525f919060409081850190868401855b82811015612071578151805164ffffffffff16855286810151878601528581015186860152606080820151908601526080908101519085015260a09093019290850190600101612026565b5091979650505050505050565b600181811c9082168061209257607f821691505b6020821081036120b057634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610486576104866120b6565b6020808252603a908201527f4f6173653a204461696c7920646174612075706461746520696e636f6d706c6560408201527f74652c2063616c6c207570646174654461696c79446174612829000000000000606082015260800190565b5f64ffffffffff808316818103612153576121536120b6565b6001019392505050565b8082028115828204841417610486576104866120b6565b5f8261218e57634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffd5b81810381811115610486576104866120b6565b5f600182016121cb576121cb6120b6565b5060010190565b634e487b7160e01b5f52603160045260245ffd5b634e487b7160e01b5f52602160045260245ffdfea2646970667358221220111f3bb3d96f253e4b108a2bb38a7cf98633167ff954618312c447ede28f1dbc64736f6c6343000814003345711452762d9dab167c16cc1f8672d6b3102251471bbf07113c1e1134d39eea
Deployed ByteCode
0x608060405234801561000f575f80fd5b5060043610610153575f3560e01c806390de6871116100bf578063db54576f11610079578063db54576f14610335578063dd62ed3e1461033d578063dfd8bf6814610350578063f80a205714610359578063f8f8b5a314610379578063fa0ff5a0146103bc575f80fd5b806390de68711461029057806395d89b41146102af578063a457c2d7146102b7578063a9059cbb146102ca578063c3124525146102dd578063cfd049d514610313575f80fd5b8063313ce56711610110578063313ce5671461020b578063395093511461021a5780635bb58d661461022d578063662a025e1461024257806370a08231146102555780638b4729d71461027d575f80fd5b8063022466b51461015757806306fdde0314610191578063095ea7b3146101a657806318160ddd146101c957806323b872dd146101d15780632eb4a7ab146101e4575b5f80fd5b61017e7f0000000000000000000000000000000000000000000000000000000068e4ce2f81565b6040519081526020015b60405180910390f35b6101996103e3565b6040516101889190611da9565b6101b96101b4366004611e0f565b610473565b6040519015158152602001610188565b60025461017e565b6101b96101df366004611e37565b61048c565b61017e7f45711452762d9dab167c16cc1f8672d6b3102251471bbf07113c1e1134d39eea81565b60405160088152602001610188565b6101b9610228366004611e0f565b6104af565b61024061023b366004611eb5565b6104d0565b005b610240610250366004611f72565b6107ce565b61017e610263366004611f89565b6001600160a01b03165f9081526020819052604090205490565b61024061028b366004611fa2565b610cb2565b61017e61029e366004611f72565b60096020525f908152604090205481565b610199610f5f565b6101b96102c5366004611e0f565b610f6e565b6101b96102d8366004611e0f565b610fe8565b6005546006546007546008546102f39392919084565b604080519485526020850193909352918301526060820152608001610188565b6101b9610321366004611f72565b600c6020525f908152604090205460ff1681565b610240610ff5565b61017e61034b366004611fd9565b61102a565b61017e61017181565b61036c610367366004611f89565b611054565b604051610188919061200a565b61038c610387366004611e0f565b6110fa565b6040805164ffffffffff90961686526020860194909452928401919091526060830152608082015260a001610188565b600a546103cd9064ffffffffff1681565b60405164ffffffffff9091168152602001610188565b6060600380546103f29061207e565b80601f016020809104026020016040519081016040528092919081815260200182805461041e9061207e565b80156104695780601f1061044057610100808354040283529160200191610469565b820191905f5260205f20905b81548152906001019060200180831161044c57829003601f168201915b5050505050905090565b5f3361048081858561114c565b60019150505b92915050565b5f33610499858285611270565b6104a48585856112e8565b506001949350505050565b5f336104808185856104c1838361102a565b6104cb91906120ca565b61114c565b8542101580156104e05750844211155b6105315760405162461bcd60e51b815260206004820152601e60248201527f4e6f742077697468696e20746865206d696e74696e672077696e646f772e000060448201526064015b60405180910390fd5b5f888152600c602052604090205460ff16156105995760405162461bcd60e51b815260206004820152602160248201527f546869732049442068617320616c7265616479206265656e20636c61696d65646044820152601760f91b6064820152608401610528565b5f811561067057604080513360208201529081018a9052606081018990526080810188905260a081018790524660c08201523060e08201525f90610100016040516020818303038152906040528051906020012090505f610626827f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f908152601c91909152603c902090565b90506106678186868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061148a92505050565b92505050610673565b50335b60408051602081018b90526001600160a01b03831691810191909152606081018990526080810188905260a081018790525f9060c0016040516020818303038152906040528051906020012090506107208686808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152507f45711452762d9dab167c16cc1f8672d6b3102251471bbf07113c1e1134d39eea92508591506114b29050565b6107645760405162461bcd60e51b815260206004820152601560248201527424b73b30b634b21026b2b935b63290383937b7b31760591b6044820152606401610528565b5f8a8152600c60205260409020805460ff19166001179055610786338a6114c7565b604080518a815242602082015233918c917fd9cb1e2714d65a111c0f20f060176ad657496bd47a3de04ec7c3d4ca232112ac910160405180910390a350505050505050505050565b5f811161081d5760405162461bcd60e51b815260206004820152601b60248201527f4f6173653a204d7573742073617665206d6f7265207468616e203000000000006044820152606401610528565b6305f5e1008110156108715760405162461bcd60e51b815260206004820152601d60248201527f4f6173653a204d696e696d756d2073617665206973203120746f6b656e0000006044820152606401610528565b600160481b81106108d55760405162461bcd60e51b815260206004820152602860248201527f4f6173653a20416d6f756e7420746f6f206c6172676520666f72206576656e74604482015267207061636b696e6760c01b6064820152608401610528565b335f908152602081905260409020548111156109335760405162461bcd60e51b815260206004820152601a60248201527f4f6173653a20696e73756666696369656e742062616c616e63650000000000006044820152606401610528565b335f908152600b60205260409020546064116109ae5760405162461bcd60e51b815260206004820152603460248201527f4f6173653a205573657220686173207265616368656420746865206d6178696d604482015273756d206e756d626572206f6620736176696e677360601b6064820152608401610528565b6109b6611d7f565b6109be611d7f565b6109c88282611584565b5f6109da836109d56115dc565b61161a565b9050806109f95760405162461bcd60e51b8152600401610528906120dd565b600a80545f91908290610a129064ffffffffff1661213a565b91906101000a81548164ffffffffff021916908364ffffffffff160217905590505f845f0151866305f5e100610a48919061215d565b610a529190612174565b90505f8111610ab95760405162461bcd60e51b815260206004820152602d60248201527f4f6173653a20416d6f756e7420746f6f20736d616c6c20666f7220637572726560448201526c6e74207368617265207261746560981b6064820152608401610528565b600160481b8110610b1d5760405162461bcd60e51b815260206004820152602860248201527f4f6173653a2053686172657320746f6f206c6172676520666f72206576656e74604482015267207061636b696e6760c01b6064820152608401610528565b610b273387611721565b5f85608001516001610b3991906120ca565b90505f610b48610171836120ca565b9050600b5f336001600160a01b03166001600160a01b031681526020019081526020015f206040518060a001604052808664ffffffffff1681526020018581526020018a815260200184815260200183815250908060018154018082558091505060019003905f5260205f2090600502015f909190919091505f820151815f015f6101000a81548164ffffffffff021916908364ffffffffff1602179055506020820151816001015560408201518160020155606082015181600301556080820151816004015550508787604001818151610c2391906120ca565b905250606087018051849190610c3a9083906120ca565b90525060405164ffffffffff80861691339142166dffffffffffffffffff000000000060288d901b161768ffffffffffffffffff60701b607088901b1617907f390cc673cefaad1ca89a4ddf40d5a3199e7ffab1ad3d1c408fa22e7e473102a7905f90a4610ca88787611849565b5050505050505050565b610cba611d7f565b610cc2611d7f565b610ccc8282611584565b5f610cd56115dc565b90505f610ce2848361161a565b905080610d015760405162461bcd60e51b8152600401610528906120dd565b335f908152600b602052604081208054909103610d535760405162461bcd60e51b815260206004820152601060248201526f4f6173653a206e6f20736176696e677360801b6044820152606401610528565b80548710610d995760405162461bcd60e51b815260206004820152601360248201527209ec2e6ca7440d2dcecc2d8d2c840d2dcc8caf606b1b6044820152606401610528565b5f818881548110610dac57610dac612193565b5f9182526020909120600590910201805490915064ffffffffff888116911614610e185760405162461bcd60e51b815260206004820152601760248201527f4f6173653a20736176696e674964206d69736d617463680000000000000000006044820152606401610528565b806004015486608001511015610e705760405162461bcd60e51b815260206004820152601860248201527f4f6173653a20736176696e67206e6f74206d61747572656400000000000000006044820152606401610528565b5f6305f5e100875f01518360010154610e89919061215d565b610e939190612174565b8254600184015460405192935064ffffffffff918216923392607086901b68ffffffffffffffffff60701b1660289390931b6dffffffffffffffffff000000000016429091161791909117907ff0019f5bb966673bc3d326e7d459457783cb80b7edb41cf11164fbc23e9d2f93905f90a4816002015487604001818151610f1a91906121a7565b9052506001820154606088018051610f339083906121a7565b905250610f40838a6118ac565b610f4a8787611849565b610f5433826114c7565b505050505050505050565b6060600480546103f29061207e565b5f3381610f7b828661102a565b905083811015610fdb5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610528565b6104a4828686840361114c565b5f336104808185856112e8565b610ffd611d7f565b611005611d7f565b61100f8282611584565b61101b826109d56115dc565b506110268282611849565b5050565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0381165f908152600b60209081526040808320805482518185028101850190935280835260609492939192909184015b828210156110ef575f8481526020908190206040805160a08101825260058602909201805464ffffffffff1683526001808201548486015260028201549284019290925260038101546060840152600401546080830152908352909201910161108b565b505050509050919050565b600b602052815f5260405f208181548110611113575f80fd5b5f9182526020909120600590910201805460018201546002830154600384015460049094015464ffffffffff9093169550909350919085565b6001600160a01b0383166111ae5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610528565b6001600160a01b03821661120f5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610528565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f61127b848461102a565b90505f1981146112e257818110156112d55760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610528565b6112e2848484840361114c565b50505050565b6001600160a01b03831661134c5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610528565b6001600160a01b0382166113ae5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610528565b6001600160a01b0383165f90815260208190526040902054818110156114255760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610528565b6001600160a01b038481165f81815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36112e2565b5f805f806114988686611996565b9250925092506114a882826119df565b5090949350505050565b5f826114be8584611a97565b14949350505050565b6001600160a01b03821661151d5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610528565b8060025f82825461152e91906120ca565b90915550506001600160a01b0382165f81815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60055482526006546020830152600754604083015260085460608301526115a96115dc565b60808381019182528351835260208085015190840152604080850151908401526060938401519383019390935251910152565b5f6201518061160b7f0000000000000000000000000000000000000000000000000000000068e4ce2f426121a7565b6116159190612174565b905090565b5f808360200151831161162d575f61163c565b602084015161163c90846121a7565b9050805f0361164f576001915050610486565b5f610171821161165f5781611663565b6101715b90505f81866020015161167691906120ca565b60208701519091505b818110156116d25760408051602081019091528751815260095f6116a48460016120ca565b815260208101919091526040015f20905190556116c087611ae3565b806116ca816121ba565b91505061167f565b5060208681018290528651604051908152829142917f8caa10406f5fa75d56a3b35f15b317829cd60a331e7048de76e042d088203bfb910160405180910390a350505060209092015114919050565b6001600160a01b0382166117815760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610528565b6001600160a01b0382165f90815260208190526040902054818110156117f45760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610528565b6001600160a01b0383165f818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101611263565b8051825114158061186257508060200151826020015114155b8061187557508060400151826040015114155b8061188857508060600151826060015114155b15611026575080516005556020810151600655604081015160075560600151600855565b81545f906118bc906001906121a7565b9050808214611947578281815481106118d7576118d7612193565b905f5260205f2090600502018383815481106118f5576118f5612193565b5f91825260209091208254600590920201805464ffffffffff191664ffffffffff9092169190911781556001808301549082015560028083015490820155600380830154908201556004918201549101555b82805480611957576119576121d2565b5f8281526020812060055f1990930192830201805464ffffffffff19168155600181018290556002810182905560038101829055600401559055505050565b5f805f83516041036119cd576020840151604085015160608601515f1a6119bf88828585611bcd565b9550955095505050506119d8565b505081515f91506002905b9250925092565b5f8260038111156119f2576119f26121e6565b036119fb575050565b6001826003811115611a0f57611a0f6121e6565b03611a2d5760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115611a4157611a416121e6565b03611a625760405163fce698f760e01b815260048101829052602401610528565b6003826003811115611a7657611a766121e6565b03611026576040516335e2f38360e21b815260048101829052602401610528565b5f81815b8451811015611adb57611ac782868381518110611aba57611aba612193565b6020026020010151611c95565b915080611ad3816121ba565b915050611a9b565b509392505050565b5f611aed82611cc4565b9050805f03611afa575050565b5f611b0c8266014f9a81ec1000612174565b90505f633b9aca008211611b5f5761016d611b28600284612174565b611b36906305f5e1006120ca565b611b44846305f5e10061215d565b611b4e9190612174565b611b589190612174565b9050611ba0565b61016d611b6d600384612174565b611b7b906305f5e1006120ca565b611b89846305f5e10061215d565b611b939190612174565b611b9d9190612174565b90505b6305f5e100611baf82826120ca565b8551611bbb919061215d565b611bc59190612174565b909352505050565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611c0657505f91506003905082611c8b565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611c57573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116611c8257505f925060019150829050611c8b565b92505f91508190505b9450945094915050565b5f818310611caf575f828152602084905260409020611cbd565b5f8381526020839052604090205b9392505050565b5f80611ccf60025490565b90505f83606001515f1480611ce357508351155b15611cef57505f611d12565b835160608501516305f5e10091611d059161215d565b611d0f9190612174565b90505b5f611d1d82846120ca565b9050805f03611d3057505f949350505050565b815f03611d4157505f949350505050565b5f81611d51846305f5e10061215d565b611d5b9190612174565b9050620186a0811015611d765750620186a095945050505050565b95945050505050565b6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b5f6020808352835180828501525f5b81811015611dd457858101830151858201604001528201611db8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114611e0a575f80fd5b919050565b5f8060408385031215611e20575f80fd5b611e2983611df4565b946020939093013593505050565b5f805f60608486031215611e49575f80fd5b611e5284611df4565b9250611e6060208501611df4565b9150604084013590509250925092565b5f8083601f840112611e80575f80fd5b50813567ffffffffffffffff811115611e97575f80fd5b602083019150836020828501011115611eae575f80fd5b9250929050565b5f805f805f805f8060c0898b031215611ecc575f80fd5b88359750602089013596506040890135955060608901359450608089013567ffffffffffffffff80821115611eff575f80fd5b818b0191508b601f830112611f12575f80fd5b813581811115611f20575f80fd5b8c60208260051b8501011115611f34575f80fd5b6020830196508095505060a08b0135915080821115611f51575f80fd5b50611f5e8b828c01611e70565b999c989b5096995094979396929594505050565b5f60208284031215611f82575f80fd5b5035919050565b5f60208284031215611f99575f80fd5b611cbd82611df4565b5f8060408385031215611fb3575f80fd5b82359150602083013564ffffffffff81168114611fce575f80fd5b809150509250929050565b5f8060408385031215611fea575f80fd5b611ff383611df4565b915061200160208401611df4565b90509250929050565b602080825282518282018190525f919060409081850190868401855b82811015612071578151805164ffffffffff16855286810151878601528581015186860152606080820151908601526080908101519085015260a09093019290850190600101612026565b5091979650505050505050565b600181811c9082168061209257607f821691505b6020821081036120b057634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610486576104866120b6565b6020808252603a908201527f4f6173653a204461696c7920646174612075706461746520696e636f6d706c6560408201527f74652c2063616c6c207570646174654461696c79446174612829000000000000606082015260800190565b5f64ffffffffff808316818103612153576121536120b6565b6001019392505050565b8082028115828204841417610486576104866120b6565b5f8261218e57634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffd5b81810381811115610486576104866120b6565b5f600182016121cb576121cb6120b6565b5060010190565b634e487b7160e01b5f52603160045260245ffd5b634e487b7160e01b5f52602160045260245ffdfea2646970667358221220111f3bb3d96f253e4b108a2bb38a7cf98633167ff954618312c447ede28f1dbc64736f6c63430008140033