// File: IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
// File: IUniswapV2Pair.sol
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
// File: @openzeppelin/contracts/interfaces/draft-IERC6093.sol
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}
// File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
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);
}
// File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// File: ReflectDataModel.sol
pragma solidity ^0.8.19;
uint256 constant FEE_TIERS = 8;
struct AccountState {
uint256 balance;
bool isHighReward;
bool excludedFromRewards;
}
struct RewardCycleStat {
EnumerableSet.AddressSet regularUsers;
EnumerableSet.AddressSet boostedUsers;
}
struct RewardCycle {
uint96 taxedEth;
uint32 lastConvertedTime;
uint32 lastRewardDistributeTime;
RewardCycleStat[FEE_TIERS] stat;
}
// File: @openzeppelin/contracts/utils/cryptography/MerkleProof.sol
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.20;
/**
* @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)
}
}
}
// File: @openzeppelin/contracts/utils/Context.sol
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
}
// File: @openzeppelin/contracts/access/Ownable.sol
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// File: RewardHolderProxy.sol
pragma solidity ^0.8.19;
contract RewardHolderProxy is Ownable {
constructor ()
Ownable(msg.sender) {
}
function SendTokenBack(IERC20 token, uint256 amount) public onlyOwner {
token.transfer(msg.sender, amount);
}
}
// File: @openzeppelin/contracts/access/Ownable2Step.sol
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
// File: ReflectErc20.sol
pragma solidity ^0.8.19;
abstract contract Reflect is Ownable2Step, IERC20, IERC20Metadata, IERC20Errors {
using EnumerableSet for EnumerableSet.AddressSet;
constructor (address teamWallet, uint256 tSupply, uint256 airdropSupply, bool deployPair)
Ownable(msg.sender) {
require(_regularTax() <= BASE_POINT, "regular tax cannot be more then 100%");
require(airdropSupply <= tSupply, "Airdrop supply shall be less or equal to total supply");
TeamWallet = teamWallet;
(_tierThresholds[0], _tierThresholds[1], _tierThresholds[2], _tierThresholds[3])=
(1_0000, 7000, 3000, 900);
(_tierThresholds[4], _tierThresholds[5], _tierThresholds[6], _tierThresholds[7]) =
(600, 300, 90, 50);
(_tierPortion[0], _tierPortion[1], _tierPortion[2], _tierPortion[3]) =
(30_00, 23_00, 15_00, 11_00);
(_tierPortion[4], _tierPortion[5], _tierPortion[6], _tierPortion[7]) =
(8_00, 6_00, 4_50, 2_50);
_totalSupply = tSupply;
AirdropSupply = airdropSupply;
_approve(address(this), msg.sender, type(uint256).max);
RewardHolderProxyAddress = new RewardHolderProxy();
if (deployPair) {
DEX = IUniswapV2Pair(_uniV2Factory().createPair(address(_wethErc20()), address(this)));
DexReflectIsToken1 = DEX.token0() == address(_wethErc20());
_accounts[address(DEX)].excludedFromRewards = true;
DEX.approve(msg.sender, type(uint256).max);
} else {
DEX = IUniswapV2Pair(address(0));
DexReflectIsToken1 = false;
}
(RewardCycleData.lastConvertedTime, RewardCycleData.lastRewardDistributeTime) = (uint32(block.timestamp), uint32(block.timestamp));
_initialized = true;
}
uint256 constant TIER_THRESHOLD_BASE = 100_0000;
uint256 constant BASE_POINT = 100_00;
uint256 constant BASE_POINT_TENS = 100_000;
// 8 + 160 = 168 bits
//
bool immutable public DexReflectIsToken1;
IUniswapV2Pair immutable public DEX;
//160 bits
address public TeamWallet;
bool immutable internal _initialized;
uint256 immutable private _totalSupply;
uint256 immutable public AirdropSupply;
uint256 public Airdropped;
RewardHolderProxy immutable public RewardHolderProxyAddress;
uint24[FEE_TIERS] internal _tierThresholds;
uint16[FEE_TIERS] internal _tierPortion;
uint256 public Boosted;
RewardCycle public RewardCycleData;
mapping(address => AccountState) internal _accounts;
mapping (address => bool) public Taxable;
mapping (bytes32 => bool) public RewardRoots;
mapping (bytes32 => bool) public ClaimedReward;
mapping (bytes32 => bool) public ClaimedAirdrop;
mapping(address account => mapping(address spender => uint256)) internal _allowances;
/********************************** DEPENDENCY INJECTIONS **********************************/
function _wethErc20() internal virtual view returns(IERC20);
function _uniV2Factory() internal virtual view returns(IUniswapV2Factory);
function _regularTax() internal virtual view returns(uint256);
/*################################# END - DEPENDENCY INJECTIONS #################################*/
/********************************** GENERIC VIEW FUNCTIONs **********************************/
/**
* @dev Returns the name of the token.
*/
function name() public pure returns (string memory) {
return "$REFLECT";
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public pure returns (string memory) {
return "$REFLECT";
}
/**
* @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 pure returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/*################################# END - GENERIC VIEW FUNCTIONS #################################*/
/********************************** CORE LOGIC **********************************/
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view returns (uint256) {
return _accounts[account].balance;
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` 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 value) public virtual returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public override returns (bool) {
return _taxableTransferCore(msg.sender, to, value);
}
/**
* @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 `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public override returns (bool) {
_spendAllowance(from, msg.sender, value);
return _taxableTransferCore(from, to, value);
}
//++++++++++++++++++++++++++++++++ PRIVATE +++++++++++++++++++++
/**
* @dev Sets `value` 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.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_transferCore(address(0), account, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
// internal (core) version of transfer
function _transferCore(address from, address to, uint256 value) internal {
if (from != address(0)) {
uint256 fromBalance = _accounts[from].balance;
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
uint256 newBalance;
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
newBalance = fromBalance - value;
//updating balance and reward cycle
_accounts[from].balance = newBalance;
}
//afterwards we need to refresh user stats
_updateWalletStat(from, fromBalance, newBalance);
}
if (to == address(0)) {
require(false, "Burning token isn't possible");
} else {
uint256 newBalance;
uint256 initBalance;
initBalance = _accounts[to].balance;
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
newBalance = value + initBalance;
_accounts[to].balance = newBalance;
}
//afterwards we need to refresh user stats
_updateWalletStat(to, initBalance, newBalance);
}
emit Transfer(from, to, value);
}
/// @notice updates user' reward' tier information to match new balance
/// @param wallet user wallet address
/// @param initBalance old balance
/// @param newBalance new balance
function _updateWalletStat(address wallet, uint256 initBalance, uint256 newBalance) internal {
(bool userBoosted, bool userExcluded) = (_accounts[wallet].isHighReward, _accounts[wallet].excludedFromRewards);
if ((wallet == address(this)) || userExcluded)
return;
(uint8 initialTier, bool initTierFound) = _getIndexTierByBalance(initBalance);
(uint8 newTier, bool newTierFound) = _getIndexTierByBalance(newBalance);
// if came up/down from rewards tires
// or
// change in tiers
if ((initTierFound != newTierFound) || (initTierFound && newTierFound && (initialTier != newTier))) {
if (initTierFound) {
if (userBoosted) {
RewardCycleData.stat[initialTier].boostedUsers.remove(wallet);
} else {
RewardCycleData.stat[initialTier].regularUsers.remove(wallet);
}
}
if (newTierFound) {
if (userBoosted) {
RewardCycleData.stat[newTier].boostedUsers.add(wallet);
} else {
RewardCycleData.stat[newTier].regularUsers.add(wallet);
}
}
}
}
/// @notice Establishes tier by given balance
/// @param balance balance to check
/// @return tier and search success flag. Tier will be equal to actual tier, if found, or to type(uin8).max = 255, if doesn't
function _getIndexTierByBalance(uint256 balance) private view returns (uint8, bool) {
unchecked {
uint256 share = balance * TIER_THRESHOLD_BASE / _totalSupply;
// using binary tree checks to lower & stable gas for all tires
// it takes 3-4 checks to make a decision
/*
* t_i- _tierThresholds[i]
* b - balance
* t_3
* | |
* +------------------+ +---------------+
* / \
* / \
* b < t_3 / \ b >= t_3
* / \
* / \
* t_6 t_1
* / \ / \
* / \ / \
* / \ / \
* / \ / \
* b < t_6 / \ b >= t_6 b < t_1 / \ b >= t_1
* / \ / \
* / \ / \
* / \ / \
* / \ / \
* / \ / \
* t_7 t_4 t_2 t_0
* / \ / \ / \ / \
* b < t_7 / \ b >= t_7 b < t_4 / \ b >= t_4 b < t_2 / \ b >= t_2 b < t_0 / \ b >= t_0
* / \ / \ / \ / \
* NF 7 t_5 4 3 2 1 0
* / \
* b < t_5 / \ b >= 5
* / \
* 6 5
**/
if (share < _tierThresholds[3]) {
if (share < _tierThresholds[6]) {
if (share < _tierThresholds[7])
return (type(uint8).max, false);
else
return (7, true);
} else {
if (share < _tierThresholds[4]) {
if (share < _tierThresholds[5])
return (6, true);
else
return (5, true);
} else
return (4, true);
}
} else {
if (share < _tierThresholds[1]) {
if (share < _tierThresholds[2])
return (3, true);
else
return (2, true);
} else {
if (share < _tierThresholds[0])
return (1, true);
else
return (0, true);
}
}
}
/*
//this is old unoptimised version:
unchecked {
for (uint256 j = 0; j < FEE_TIERS; ++j) {
if (share >= _tierThresholds[j]) {
return (uint8(j), true);
}
}
}
return (type(uint8).max, false);
//*/
}
// perfroms a token transfer with taxation
/// @notice Perfroms a token transfer with taxation
/// @param from source wallet
/// @param to destination wallet
/// @param value amount of tokens
/// @return transfer status
function _taxableTransferCore(address from, address to, uint256 value) private returns (bool) {
uint256 taxRate = 0;
// Do taxing only on whitelisted wallets
if (Taxable[to] || Taxable[from])
taxRate = _regularTax();
//calculating total amount of taxation
uint256 taxValue;
//overflow must never happen
//taxRate <= BASE_POINT
unchecked {
taxValue = value * taxRate / BASE_POINT;
value -= taxValue;
}
if (taxValue > 0)
_transferCore(from, address(this), taxValue);
//main transfer
_transferCore(from, to, value);
return true;
}
/*################################# END - CORE LOGIC #################################*/
/********************************** REWARD ALT LOGIC **********************************/
/// @notice Allows to claim rewards with Merkle Proof (alternate logic for main one)
function ClaimRewardWithProof(bytes32 root, bytes32[] calldata proof, uint256 amount) external {
require(RewardRoots[root], "Unrecognized reward");
bytes32 leaf = keccak256(abi.encode(msg.sender, amount));
require (MerkleProof.verifyCalldata(proof, root, leaf), "you're not part of this reward or input is wrong");
require (!ClaimedReward[leaf] , "You've claimed it ;-)");
ClaimedReward[leaf] = true;
_wethErc20().transfer(msg.sender, amount);
}
/// @notice Distributes rewards by the owner (alternate logic for main one)
function DistributeReward(address[] calldata addresses, uint256[] calldata amounts, uint256 gasLimit) external onlyOwner returns(uint256) {
for (uint256 i = 0; i < addresses.length; ++i) {
_wethErc20().transfer(addresses[i], amounts[i]);
if (gasleft() < gasLimit)
return i;
}
return addresses.length;
}
/// @notice Enables claim rewards with given root of Merkle tree (alternate logic for main one)
function EnableReward(bytes32 root) external onlyOwner {
RewardRoots[root] = true;
}
/// @notice Disables claim rewards with given root of Merkle tree (alternate logic for main one)
function DisableReward(bytes32 root) external onlyOwner {
RewardRoots[root] = false;
}
/*################################# END - REWARD ALT LOGIC #################################*/
/********************************** AIRDROP LOGIC **********************************/
/// @notice Airdrops pre-sale token amount to the memebers
function Airdrop(address[] calldata addresses, uint256[] calldata amounts, uint256 gasLimit) external onlyOwner returns(uint256) {
uint256 airdropped = Airdropped;
for (uint256 i = 0; i < addresses.length; ++i) {
airdropped += amounts[i];
require(airdropped <= AirdropSupply, "Supply overflow" );
require(amounts[i] > 0, "Amount must be non 0");
_mint(addresses[i], amounts[i]);
if (gasleft() < gasLimit)
{
Airdropped = airdropped;
return i;
}
}
Airdropped = airdropped;
return addresses.length;
}
/*################################# END - AIRDROP LOGIC #################################*/
/********************************** PRIVATE FUNCS **********************************/
/// @notice Fork of uni v2 estimations of tokens out
function _uni2GetAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256 amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
unchecked { //as it was in Solidity 0.5.0
amountOut = numerator / denominator;
}
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
address oldOwner = owner();
super._transferOwnership(newOwner);
if (_initialized) {
_approve(address(this), oldOwner, 0);
_approve(address(this), newOwner, type(uint256).max);
if (address(DEX) != address(0)) {
DEX.approve(oldOwner, 0);
DEX.approve(newOwner, type(uint256).max);
}
}
}
/// @notice Distributes rewards across userbase and launches new reward cycle
/// @param rewardDistroShare % of rewards wich actuall will be distributed from reward pool
function _launchNewRewardCycleCore(uint256 rewardDistroShare) private {
require(rewardDistroShare <= BASE_POINT, "rewardDistroShare cannot exceed 100%");
uint256 taxed = RewardCycleData.taxedEth * rewardDistroShare / BASE_POINT;
(RewardCycleData.taxedEth, RewardCycleData.lastRewardDistributeTime) = (0, uint32(block.timestamp));
for (uint256 i = 0; i < FEE_TIERS; ++i) {
RewardCycleStat storage rewStat = RewardCycleData.stat[i];
uint256 tierPool = _tierPortion[i] * taxed / BASE_POINT;
uint256 bstLen = rewStat.boostedUsers.length();
uint256 regLen = rewStat.regularUsers.length();
uint256 shareRatio;
uint256 reward;
if (regLen + bstLen == 0)
continue;
unchecked {
/*
//uint256 nominator = (100 - boosted) * BASE_POINT_TENS; //(using 2's complaint we can get)
//(101 + ~boosted) * BASE_POINT_TENS
uint256 denominator = 100 * (regular + boosted);
uint256 shareRatio = nominator / denominator;
//*/
shareRatio = ((101 + ~bstLen) * BASE_POINT_TENS) / ( (regLen + bstLen) * 100);
reward = shareRatio * tierPool / BASE_POINT_TENS;
}
for (uint256 j = 0; j < regLen; j++) {
address rewardWallet = rewStat.regularUsers.at(j);
unchecked {
_wethErc20().transfer(rewardWallet, reward);
}
}
unchecked {
shareRatio += 1_000; //+ 1%
reward = shareRatio * tierPool / BASE_POINT_TENS;
}
if (reward == 0)
continue;
for (uint256 j = 0; j < bstLen; j++) {
address rewardWallet = rewStat.boostedUsers.at(j);
unchecked {
_wethErc20().transfer(rewardWallet, reward);
}
}
}
}
/// @notice Swaps rewards and fixes them in ETH
/// @param priceLimitNE28 price limit with slippage
function _fixEthRewardsCore(uint256 priceLimitNE28) private {
uint256 sellAmount = _accounts[address(this)].balance;
uint256 amountOut0 = 0;
uint256 amountOut1 = 0;
require (sellAmount > 0, "Empty balance");
{
uint256 expectedOut = sellAmount * priceLimitNE28 / 1e28; //close to Q96
(uint112 reserveIn, uint112 reserveOut, ) = DEX.getReserves();
if (DexReflectIsToken1)
(reserveIn, reserveOut) = (reserveOut, reserveIn) ;
if (DexReflectIsToken1)
amountOut0 = _uni2GetAmountOut(sellAmount, reserveIn, reserveOut);
else
amountOut1 = _uni2GetAmountOut(sellAmount, reserveIn, reserveOut);
require((amountOut0 | amountOut1) >= expectedOut, "Price slippage too small");
}
RewardHolderProxy holder = RewardHolderProxyAddress;
//sending without taxes
_transferCore(address(this), address(DEX), sellAmount);
DEX.swap(amountOut0, amountOut1, address(holder), new bytes(0));
unchecked {
uint256 totalTaxed = amountOut0 | amountOut1;
uint256 rewAmount = totalTaxed / 2;
holder.SendTokenBack(_wethErc20(), totalTaxed);
//uint96 is quite big for eth
//uint32 is ok up to 19 January 2038, at 03:14:07 UTC
//overflow is ok
(RewardCycleData.taxedEth, RewardCycleData.lastConvertedTime) =
(uint96(RewardCycleData.taxedEth + rewAmount), uint32(block.timestamp));
_wethErc20().transfer(TeamWallet, totalTaxed - rewAmount);
}
}
/*################################# END - PRIVATE FUNCS #################################*/
/// @notice Allows to observe members stat at given reward tier
/// @param tier reward tier
/// @return Documents amount of members per reward tier
function GetRewardCycleTierStat(uint256 tier) external view returns(uint256, uint256) {
return (
RewardCycleData.stat[tier].regularUsers.length(),
RewardCycleData.stat[tier].boostedUsers.length()
);
}
/// @notice Returns whole view of memebers per given reward tier
/// @param tier reward tier
/// @param boosted boosted/regular list to fetch
/// @return list of wallets
function GetRewardCycleMembersAtTier(uint256 tier, bool boosted) external view returns (address[] memory) {
uint256 recordsLen;
if (boosted)
recordsLen = RewardCycleData.stat[tier].boostedUsers.length();
else
recordsLen = RewardCycleData.stat[tier].regularUsers.length();
return GetRewardCycleMembersAtTier(tier, boosted, recordsLen, 0);
}
/// @notice Returns paged view of memebers per given reward tier and page
/// @param tier reward tier
/// @param boosted boosted/regular list to fetch
/// @param pageSize page size
/// @param page page number
/// @return list of wallets
function GetRewardCycleMembersAtTier(uint256 tier, bool boosted, uint256 pageSize, uint256 page) public view returns (address[] memory) {
uint256 recordsLen;
if (boosted)
recordsLen = RewardCycleData.stat[tier].boostedUsers.length();
else
recordsLen = RewardCycleData.stat[tier].regularUsers.length();
uint256 indexStart = pageSize * page; //inclusive
uint256 indexEnd = indexStart + page; //non-inlusive
if (indexStart >= recordsLen)
return new address[](0);
if (indexEnd > recordsLen)
indexEnd = recordsLen;
address[] memory result = new address[](indexEnd - indexStart);
for (uint256 i = 0; i < result.length; i++)
if (boosted)
result[i] = RewardCycleData.stat[tier].boostedUsers.at(i + indexStart);
else
result[i] = RewardCycleData.stat[tier].regularUsers.at(i + indexStart);
return result;
}
/// @notice Checks if given memebr is eligible for rewards and has been bosted
/// @param wallet An user wallet address
/// @return Tire index (or type(uint256).max if not found), boost status and search sucess flags
function GetWalletRewardTier(address wallet) external view returns (uint256, bool, bool) {
for (uint256 tire = 0; tire < FEE_TIERS; tire++) {
if (RewardCycleData.stat[tire].regularUsers.contains(wallet))
return (tire, false, true);
if (RewardCycleData.stat[tire].boostedUsers.contains(wallet))
return (tire, true, true);
}
return (type(uint256).max, false, false);
}
/// @notice Adds liqudity to the pair/pool and opens trading
/// @return pair address
function LaunchUniV2Pool() external onlyOwner returns(address) {
require(AirdropSupply < _totalSupply, "0 supply for the pool");
//We can have total supply only once, so second time it must crash
_mint(address(DEX), _totalSupply - AirdropSupply);
_wethErc20().transfer(address(DEX), _wethErc20().balanceOf(address(this)));
DEX.mint(address(this));
DEX.approve(msg.sender, type(uint256).max);
Taxable[address(DEX)] = true;
return address(DEX);
}
/// @notice Distributes rewards across userbase and launches new reward cycle (Public version, allowed to be called only in 30 minutes since last distribution happened)
function LaunchNewRewardCycle() external {
require((RewardCycleData.lastRewardDistributeTime + 2 days) <= block.timestamp, "Only availabe in 2 days since last call");
_launchNewRewardCycleCore(BASE_POINT);
}
/// @notice Distributes rewards across userbase and launches new reward cycle
/// @param priceLimitNE28 price limit with slippage if swap will happen, otherwise ignored
/// @param skipSwap if swap needs to be skipped
/// @param rewardDistroShare % of rewards wich actuall will be distributed from reward pool
function LaunchNewRewardCycleOwner(uint256 priceLimitNE28, bool skipSwap, uint256 rewardDistroShare) external onlyOwner {
if (!skipSwap)
FixEthRewards(priceLimitNE28);
_launchNewRewardCycleCore(rewardDistroShare);
}
/// @notice Swaps rewards and fixes them in ETH
/// @param priceLimitNE28 price limit with slippage
function FixEthRewards(uint256 priceLimitNE28) public {
require((RewardCycleData.lastConvertedTime + 2 days) < block.timestamp, "Only availabe in 2 days since last call");
_fixEthRewardsCore(priceLimitNE28);
}
/// @notice Swaps rewards and fixes them in ETH
/// @param priceLimitNE28 price limit with slippage
function FixEthRewardsOwner(uint256 priceLimitNE28) public onlyOwner {
_fixEthRewardsCore(priceLimitNE28);
}
/// @notice Updates team wallet address
function UpdateTeamWallet(address teamWallet) external onlyOwner {
TeamWallet = teamWallet;
}
/// @notice Updates whitelisting for the tax
function UpdateWhitelisting(address add, bool taxStatus) external onlyOwner {
Taxable[add] = taxStatus;
}
/// @notice Boosts the user wallet
function BoostWallet(address wallet) external onlyOwner {
require(!_accounts[wallet].isHighReward, "Account could be boosted only once");
require(Boosted < 10, "No more then 10 users could be boosted");
++Boosted;
_accounts[wallet].isHighReward = true;
(uint8 tier, bool tierFound) = _getIndexTierByBalance(_accounts[wallet].balance);
if (tierFound) {
RewardCycleStat storage rewstat = RewardCycleData.stat[tier];
rewstat.regularUsers.remove(wallet);
rewstat.boostedUsers.add(wallet);
}
}
/// @notice Excludes wallet from reward indexation and receiving
function ExcludeWalletFromRewards(address wallet) external onlyOwner {
uint256 balance = _accounts[wallet].balance;
if (!_accounts[wallet].excludedFromRewards) {
_accounts[wallet].excludedFromRewards = true;
(uint8 tier, bool tierFound) = _getIndexTierByBalance(balance);
if (tierFound) {
if (_accounts[wallet].isHighReward) {
RewardCycleData.stat[tier].boostedUsers.remove(wallet);
} else {
RewardCycleData.stat[tier].regularUsers.remove(wallet);
}
}
}
}
}
// File: ReflectErc20OnBase.sol
pragma solidity ^0.8.19;
contract ReflectErc20OnBase is Reflect {
constructor (address teamWallet, uint256 tSupply, uint256 airdropSupply)
Reflect(teamWallet, tSupply, airdropSupply, true)
{
}
function _wethErc20() internal override pure returns(IERC20) {
return IERC20(0x4200000000000000000000000000000000000006);
}
function _uniV2Factory() internal override pure returns(IUniswapV2Factory) {
return IUniswapV2Factory(0x71524B4f93c58fcbF659783284E38825f0622859);
}
function _regularTax() internal override pure returns(uint256) {
return 5_00;
}
}
{
"compilationTarget": {
"ReflectErc20OnBase.sol": "ReflectErc20OnBase"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 10000000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"teamWallet","type":"address"},{"internalType":"uint256","name":"tSupply","type":"uint256"},{"internalType":"uint256","name":"airdropSupply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"Airdrop","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"AirdropSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Airdropped","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"BoostWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"Boosted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimRewardWithProof","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"ClaimedAirdrop","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"ClaimedReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEX","outputs":[{"internalType":"contract IUniswapV2Pair","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DexReflectIsToken1","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"DisableReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"DistributeReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"EnableReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"ExcludeWalletFromRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"priceLimitNE28","type":"uint256"}],"name":"FixEthRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"priceLimitNE28","type":"uint256"}],"name":"FixEthRewardsOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"bool","name":"boosted","type":"bool"}],"name":"GetRewardCycleMembersAtTier","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"},{"internalType":"bool","name":"boosted","type":"bool"},{"internalType":"uint256","name":"pageSize","type":"uint256"},{"internalType":"uint256","name":"page","type":"uint256"}],"name":"GetRewardCycleMembersAtTier","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tier","type":"uint256"}],"name":"GetRewardCycleTierStat","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"GetWalletRewardTier","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LaunchNewRewardCycle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"priceLimitNE28","type":"uint256"},{"internalType":"bool","name":"skipSwap","type":"bool"},{"internalType":"uint256","name":"rewardDistroShare","type":"uint256"}],"name":"LaunchNewRewardCycleOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"LaunchUniV2Pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"RewardCycleData","outputs":[{"internalType":"uint96","name":"taxedEth","type":"uint96"},{"internalType":"uint32","name":"lastConvertedTime","type":"uint32"},{"internalType":"uint32","name":"lastRewardDistributeTime","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RewardHolderProxyAddress","outputs":[{"internalType":"contract RewardHolderProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"RewardRoots","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"Taxable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TeamWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"teamWallet","type":"address"}],"name":"UpdateTeamWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"add","type":"address"},{"internalType":"bool","name":"taxStatus","type":"bool"}],"name":"UpdateWhitelisting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]