编译器
0.8.24+commit.e11b9ed9
文件 1 的 13:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success,) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(address target, bool success, bytes memory returndata)
internal
view
returns (bytes memory)
{
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 13:Context.sol
pragma solidity ^0.8.20;
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;
}
}
文件 3 的 13:ElementHolderVault.sol
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "./interfaces/IElementNFT.sol";
import "./interfaces/IElement280.sol";
import "./lib/constants.sol";
contract ElementHolderVault is Ownable2Step {
using SafeERC20 for IERC20;
struct Cycle {
uint256 timestamp;
uint256 tokensPerMultiplier;
}
address public immutable E280;
address public immutable E280_NFT;
address public treasury;
address public devWallet;
uint256 public totalRewardPool;
uint256 public minCyclePool;
uint256 public totalRewadsPaid;
uint256 public totalE280Burned;
uint256 public currentCycle;
mapping(uint256 id => Cycle) public cycles;
mapping(uint256 tokenId => uint256) public claimedCycles;
mapping(address user => uint256) public claimed;
event CycleUpdated();
constructor(
address _E280,
address _E280_NFT,
address _owner,
address _devWallet,
address _treasury,
uint256 _minCyclePool
) Ownable(_owner) {
require(_E280 != address(0), "E280 token address not provided");
require(_E280_NFT != address(0), "E280 NFT address not provided");
require(_owner != address(0), "Owner wallet not provided");
require(_devWallet != address(0), "Dev wallet address not provided");
require(_treasury != address(0), "Treasury address not provided");
require(_minCyclePool > 0, "Minimum cycle pool not provided");
E280 = _E280;
E280_NFT = _E280_NFT;
devWallet = _devWallet;
treasury = _treasury;
minCyclePool = _minCyclePool;
}
function updateCycle() external {
require(block.timestamp > getNextCycleTime(), "Cooldown in progress");
uint256 cyclePool = getNextCyclePool();
require(cyclePool > minCyclePool, "Not enough E280 available");
unchecked {
currentCycle++;
uint256 rewardPool = _processCyclePool(cyclePool);
uint256 multiplierPool = IElementNFT(E280_NFT).multiplierPool();
cycles[currentCycle] = Cycle(block.timestamp, rewardPool / multiplierPool);
totalRewardPool += cycles[currentCycle].tokensPerMultiplier * multiplierPool;
}
emit CycleUpdated();
}
function claimRewards(uint256[] calldata tokenIds) external {
require(currentCycle != 0, "No cycle created");
(uint256[] memory timestamps, uint16[] memory multipliers) =
IElementNFT(E280_NFT).getBatchedTokensData(tokenIds, msg.sender);
uint256 totalReward;
unchecked {
for (uint256 i = 0; i < tokenIds.length; i++) {
totalReward += _processTokenId(tokenIds[i], timestamps[i], multipliers[i]);
}
claimed[msg.sender] += totalReward;
totalRewadsPaid += totalReward;
}
IERC20(E280).safeTransfer(msg.sender, totalReward);
}
function setMinCyclePool(uint256 limit) external onlyOwner {
minCyclePool = limit;
}
function setTreasury(address _address) external onlyOwner {
require(_address != address(0), "Treasury address not provided");
treasury = _address;
}
function getRewards(uint256[] calldata tokenIds, address account)
external
view
returns (bool[] memory availability, uint256 totalReward)
{
require(tokenIds.length > 0, "No tokenIds provided");
require(currentCycle != 0, "No cycles created");
availability = new bool[](tokenIds.length);
(uint256[] memory timestamps, uint16[] memory multipliers) =
IElementNFT(E280_NFT).getBatchedTokensData(tokenIds, account);
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
for (uint256 j = i + 1; j < tokenIds.length; j++) {
require(tokenId != tokenIds[j], "Duplicate token ID");
}
uint256 nftTimestamp = timestamps[i];
uint256 nftClaimedCycle = claimedCycles[tokenId];
uint256 totalTokensPerMultiplier;
uint256 startCycle = nftClaimedCycle == 0 ? _getNFTFirstCycle(nftTimestamp) : nftClaimedCycle + 1;
availability[i] = startCycle <= currentCycle;
if (startCycle <= currentCycle) {
uint256 endCycle = _applyMaxCycleProtection(startCycle);
unchecked {
for (uint256 j = startCycle; j <= endCycle; j++) {
totalTokensPerMultiplier += cycles[j].tokensPerMultiplier;
}
totalReward += totalTokensPerMultiplier * multipliers[i];
}
}
}
}
function getNextCycleTime() public view returns (uint256) {
return cycles[currentCycle].timestamp + CYCLE_INTERVAL;
}
function getNextCyclePool() public view returns (uint256) {
return IERC20(E280).balanceOf(address(this)) + totalRewadsPaid - totalRewardPool;
}
function _processTokenId(uint256 tokenId, uint256 nftTimestamp, uint16 multiplier)
internal
returns (uint256 tokenReward)
{
uint256 nftClaimedCycle = claimedCycles[tokenId];
uint256 totalTokensPerMultiplier;
uint256 startCycle = nftClaimedCycle == 0 ? _getNFTFirstCycle(nftTimestamp) : nftClaimedCycle + 1;
require(startCycle <= currentCycle, "Cycle not available");
uint256 endCycle = _applyMaxCycleProtection(startCycle);
unchecked {
for (uint256 i = startCycle; i <= endCycle; i++) {
totalTokensPerMultiplier += cycles[i].tokensPerMultiplier;
}
}
claimedCycles[tokenId] = endCycle;
unchecked {
tokenReward = totalTokensPerMultiplier * multiplier;
}
}
function _processCyclePool(uint256 pool) internal returns (uint256 rewardPool) {
IElement280 e280 = IElement280(E280);
uint256 burnPool;
uint256 devFee;
uint256 treasuryFee;
unchecked {
if (e280.presaleEnd() < block.timestamp) {
burnPool = pool * 20 / 100;
devFee = pool * 15 / 100;
treasuryFee = pool * 5 / 100;
} else {
burnPool = pool * 100 / 1000;
devFee = pool * 75 / 1000;
treasuryFee = pool * 25 / 1000;
}
totalE280Burned += burnPool;
rewardPool = pool - burnPool - devFee - treasuryFee;
}
e280.burn(burnPool);
IERC20(E280).safeTransfer(devWallet, devFee);
IERC20(E280).safeTransfer(treasury, treasuryFee);
}
function _applyMaxCycleProtection(uint256 startCycle) internal view returns (uint256) {
return
currentCycle - startCycle + 1 > MAX_CYCLES_PER_CLAIM ? startCycle + MAX_CYCLES_PER_CLAIM - 1 : currentCycle;
}
function _getNFTFirstCycle(uint256 nftTimestamp) internal view returns (uint256) {
for (uint256 i = 1; i <= currentCycle; i++) {
if (cycles[i].timestamp > nftTimestamp) return i;
}
return currentCycle + 1;
}
}
文件 4 的 13:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 5 的 13:IERC20Burnable.sol
pragma solidity ^0.8.24;
interface IERC20Burnable {
function burn(uint256 value) external;
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 6 的 13:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 7 的 13:IElement280.sol
pragma solidity ^0.8.24;
import "./IERC20Burnable.sol";
interface IElement280 is IERC20Burnable {
function presaleEnd() external returns (uint256);
function handleRedeem(uint256 amount, address receiver) external;
}
文件 8 的 13:IElementNFT.sol
pragma solidity ^0.8.24;
interface IElementNFT {
function startPresale(uint256 _presaleEnd) external;
function multiplierPool() external returns (uint256);
function getBatchedTokensData(uint256[] calldata tokenIds, address owner)
external
view
returns (uint256[] memory timestamps, uint16[] memory multipliers);
}
文件 9 的 13:ITitanOnBurn.sol
pragma solidity ^0.8.10;
interface ITitanOnBurn {
function onBurn(address user, uint256 amount) external;
}
文件 10 的 13:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 11 的 13:Ownable2Step.sol
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
文件 12 的 13:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 13 的 13:constants.sol
pragma solidity ^0.8.24;
import "../interfaces/ITitanOnBurn.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
uint8 constant NUM_ECOSYSTEM_TOKENS = 14;
address constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant TITANX = 0xF19308F923582A6f7c465e5CE7a9Dc1BEC6665B1;
address constant HYPER_ADDRESS = 0xE2cfD7a01ec63875cd9Da6C7c1B7025166c2fA2F;
address constant HELIOS_ADDRESS = 0x2614f29C39dE46468A921Fd0b41fdd99A01f2EDf;
address constant DRAGONX_ADDRESS = 0x96a5399D07896f757Bd4c6eF56461F58DB951862;
address constant BDX_ADDRESS = 0x9f278Dc799BbC61ecB8e5Fb8035cbfA29803623B;
address constant BLAZE_ADDRESS = 0xfcd7cceE4071aA4ecFAC1683b7CC0aFeCAF42A36;
address constant INFERNO_ADDRESS = 0x00F116ac0c304C570daAA68FA6c30a86A04B5C5F;
address constant HYDRA_ADDRESS = 0xCC7ed2ab6c3396DdBc4316D2d7C1b59ff9d2091F;
address constant AWESOMEX_ADDRESS = 0xa99AFcC6Aa4530d01DFFF8E55ec66E4C424c048c;
address constant FLUX_ADDRESS = 0xBFDE5ac4f5Adb419A931a5bF64B0f3BB5a623d06;
address constant DRAGONX_BURN_ADDRESS = 0x1d59429571d8Fde785F45bf593E94F2Da6072Edb;
uint256 constant PRESALE_LENGTH = 28 days;
uint256 constant COOLDOWN_PERIOD = 48 hours;
uint256 constant LP_POOL_SIZE = 200_000_000_000 ether;
uint256 constant DEV_PERCENT = 6;
uint256 constant TREASURY_PERCENT = 4;
uint256 constant BURN_PERCENT = 10;
uint256 constant PRESALE_TRANSFER_TAX_PERCENTAGE = 16;
uint256 constant TRANSFER_TAX_PERCENTAGE = 4;
uint256 constant NFT_REDEEM_TAX_PERCENTAGE = 3;
uint16 constant MAX_CYCLES_PER_CLAIM = 100;
uint32 constant CYCLE_INTERVAL = 7 days;
address constant UNISWAP_V2_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
uint24 constant POOL_FEE_1PERCENT = 10000;
bytes4 constant INTERFACE_ID_ERC165 = 0x01ffc9a7;
bytes4 constant INTERFACE_ID_ERC20 = type(IERC20).interfaceId;
bytes4 constant INTERFACE_ID_ERC721 = 0x80ac58cd;
bytes4 constant INTERFACE_ID_ERC721Metadata = 0x5b5e139f;
bytes4 constant INTERFACE_ID_ITITANONBURN = type(ITitanOnBurn).interfaceId;
{
"compilationTarget": {
"contracts/ElementHolderVault.sol": "ElementHolderVault"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_E280","type":"address"},{"internalType":"address","name":"_E280_NFT","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_devWallet","type":"address"},{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"uint256","name":"_minCyclePool","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[],"name":"CycleUpdated","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"},{"inputs":[],"name":"E280","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"E280_NFT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"claimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimedCycles","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentCycle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cycles","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"tokensPerMultiplier","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"devWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextCyclePool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextCycleTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"account","type":"address"}],"name":"getRewards","outputs":[{"internalType":"bool[]","name":"availability","type":"bool[]"},{"internalType":"uint256","name":"totalReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minCyclePool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":[{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"setMinCyclePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalE280Burned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewadsPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewardPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateCycle","outputs":[],"stateMutability":"nonpayable","type":"function"}]