Source Code
Latest 7 from a total of 7 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Wyvern Buy A... | 24405776 | 25 days ago | IN | 0 ETH | 0.00006069 | ||||
| Stake | 24080086 | 70 days ago | IN | 0 ETH | 0.00002833 | ||||
| Claim | 24060304 | 73 days ago | IN | 0 ETH | 0.00001786 | ||||
| Stake | 23638669 | 132 days ago | IN | 0 ETH | 0.00050049 | ||||
| Stake | 23242479 | 188 days ago | IN | 0 ETH | 0.00029724 | ||||
| Stake | 22182290 | 336 days ago | IN | 0 ETH | 0.00121198 | ||||
| Set Wyvern Buy A... | 22176880 | 337 days ago | IN | 0 ETH | 0.00002885 |
Latest 5 internal transactions
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
WyvernVault
Compiler Version
v0.8.20+commit.a1b79de6
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../interfaces/ITITANX.sol";
import "../libs/TitanStaker.sol";
import "../libs/constant.sol";
/**
* @notice Central vault managing staking in TitanX and reward distribution
*/
contract WyvernVault is Ownable2Step, ReentrancyGuard {
using SafeERC20 for IERC20;
using SafeERC20 for ITitanX;
address public titanBuyAddress;
address public wyvernBuyAndBurnAddress;
uint256 public vault;
uint256 public titanStakerContractsCount;
address public activeTitanStakerContract;
uint256 public titanStakingStartTimestamp;
uint256 public nextTitanStakeTimestamp;
uint256 public totalTitanStaked;
uint256 public totalTitanUnstaked;
uint256 public totalEthRewardsCollected;
mapping(uint256 => address) public titanStakerContracts;
mapping(address => uint256) private genesisVault;
mapping(address => bool) private ethDepositorWhitelist;
mapping(address => bool) private titanStakerWhitelist;
event TitanStakerCreated(uint256 indexed stakerContractId, address indexed stakerContractAddress);
event Collected(
address indexed caller,
uint256 indexed totalCollected,
uint256 titanBuy,
uint256 wyvernBuyAndBurn,
uint256 genesis,
uint256 rewardFee
);
event TitanStakeStarted(address indexed titanStakerAddress, uint256 amount);
event TitanStakesEnded(address indexed titanStakerAddress, uint256 amount);
error InvalidWyvernAddress();
error NoMoreStakesAllowed();
error NoPendingEthRewards();
error NoTokensToStake();
error NewTitanStakerNotNeeded();
error InvalidAddress();
error CooldownPeriodActive();
error InvalidCaller();
error InsufficientBalanceForCallerReward();
/**
* @notice Begins a new TitanX staking session if conditions are met
* @dev Initiates staking when vault reaches threshold or after cooldown
*/
function stake() external {
TitanStaker titanStaker = TitanStaker(
payable(activeTitanStakerContract)
);
if (titanStaker.activeTitanXStakes() >= TITANX_MAX_STAKE_PER_WALLET) {
revert NoMoreStakesAllowed();
}
updateVault();
uint256 vault_ = vault;
if (vault_ == 0) {
revert NoTokensToStake();
}
if (vault_ >= TITANX_BPB_MAX_TITAN) {
_startTitanXStake();
nextTitanStakeTimestamp = block.timestamp + 7 days;
} else {
if (block.timestamp < nextTitanStakeTimestamp) {
revert CooldownPeriodActive();
}
_startTitanXStake();
nextTitanStakeTimestamp = block.timestamp + 7 days;
}
}
/**
* @notice Harvests and splits ETH yields across protocol components
* @dev Allocates: 80% TitanX purchase (compound or send to WyvernX stakers), 5% genesis, 5% burn, 7% legacy, 3% caller
*/
function claim() external nonReentrant returns (uint256 collectedAmount) {
if (msg.sender != tx.origin) revert InvalidCaller();
uint256 ethBalanceBefore = address(this).balance;
ITitanX(TITANX_ADDRESS).triggerPayouts();
uint256 triggerPayoutsIncentiveFee = address(this).balance -
ethBalanceBefore;
for (uint256 ids; ids < titanStakerContractsCount; ids++) {
TitanStaker titanStaker = TitanStaker(
payable(titanStakerContracts[ids])
);
collectedAmount += titanStaker.claim();
}
if (collectedAmount == 0) revert NoPendingEthRewards();
// Calculate the genesis share (5%).
uint256 genesisShare = (collectedAmount * 500) / PERCENTAGE_BASE_VAULT;
// Calculate the tip for the caller (3%).
uint256 incentiveFee = (collectedAmount * REWARD_FEE) / PERCENTAGE_BASE_VAULT;
// Calculate the Buy and Burn share for Wyvern (7%).
uint256 buyAndBurnWyvern = (collectedAmount * 700) / PERCENTAGE_BASE_VAULT;
// Calculate the Titan buy share (remainder, ~85%).
uint256 buyTitanXshare = collectedAmount -
genesisShare -
incentiveFee -
buyAndBurnWyvern;
genesisVault[address(0)] += genesisShare;
Address.sendValue(payable(wyvernBuyAndBurnAddress), buyAndBurnWyvern);
Address.sendValue(payable(titanBuyAddress), buyTitanXshare);
address sender = _msgSender();
uint256 callerReward = incentiveFee + triggerPayoutsIncentiveFee;
if (address(this).balance < callerReward) {
revert InsufficientBalanceForCallerReward();
}
Address.sendValue(
payable(sender),
callerReward
);
totalEthRewardsCollected += collectedAmount;
emit Collected(
sender,
collectedAmount,
buyTitanXshare,
buyAndBurnWyvern,
genesisShare,
callerReward
);
}
constructor(
address titanBuyAddress_,
address wyvernBuyAndBurnAdddress_,
bytes32 deploymentKey_
) Ownable(msg.sender) {
if (titanBuyAddress_ == address(0)) revert InvalidAddress();
if (wyvernBuyAndBurnAdddress_ == address(0)) revert InvalidAddress();
titanBuyAddress = titanBuyAddress_;
wyvernBuyAndBurnAddress = wyvernBuyAndBurnAdddress_;
ethDepositorWhitelist[TITANX_ADDRESS] = true;
_createTitanStaker(deploymentKey_);
}
/**
* @notice Creates a new TitanStaker contract if the current one is full.
* @dev Uses a user-provided salt for unique deployment.
* @param deploymentKey Salt for contract creation.
*/
function createNewTitanStaker(bytes32 deploymentKey) external {
TitanStaker titanStaker = TitanStaker(
payable(activeTitanStakerContract)
);
if (titanStaker.activeTitanXStakes() < TITANX_MAX_STAKE_PER_WALLET) revert NewTitanStakerNotNeeded();
_createTitanStaker(deploymentKey);
}
/**
* @notice Totals stakes across all TitanStaker contracts.
* @dev Iterates and sums stakes from each contract.
* @return totalActiveStakes Total stakes across contracts.
*/
function totalActiveStakesInTitanStakers() external view returns (uint256 totalActiveStakes) {
for (uint256 ids; ids < titanStakerContractsCount; ids++) {
TitanStaker titanStaker = TitanStaker(
payable(titanStakerContracts[ids])
);
totalActiveStakes += titanStaker.activeTitanXStakes();
}
}
/**
* @dev Scans all TitanStaker contracts for completed stakes.
* Returns true, titanStaker address, and stake ID if found.
* Otherwise, returns false, zero address, and zero ID.
* @return hasCompletedStakes True if a completed stake exists.
* @return titanStakerAddress Address of the titanStaker with a completed stake.
* @return stakeId ID of the completed stake, or zero if none.
*/
function getAllCompletedStakes() external view returns (bool hasCompletedStakes, address titanStakerAddress, uint256 stakeId)
{
for (uint256 ids; ids < titanStakerContractsCount; ids++) {
address titanStakerAddress_ = titanStakerContracts[ids];
TitanStaker titanStaker = TitanStaker(payable(titanStakerAddress_));
(bool isCompleted, uint256 completedStakeId) = titanStaker
.checkForCompletedStakes();
if (isCompleted) return (true, titanStakerAddress_, completedStakeId);
}
return (false, address(0), 0);
}
/**
* @notice Sets the initial timestamps for staking in TitanX Protocol.
*/
function allowTitanStaking() external {
address wyvernBuyAndBurnAddress_ = wyvernBuyAndBurnAddress;
require(msg.sender == wyvernBuyAndBurnAddress_, "unauthorized to allowTitanStaking");
uint256 nowTimestamp = block.timestamp;
uint256 secondsToMidnight = SECONDS_IN_DAY - (nowTimestamp % SECONDS_IN_DAY);
titanStakingStartTimestamp = nowTimestamp + secondsToMidnight;
nextTitanStakeTimestamp = titanStakingStartTimestamp + STAKE_COOLDOWN_PERIOD;
}
modifier onlyTitanStaker() {
require(titanStakerWhitelist[_msgSender()], "not permitted");
_;
}
/**
* @dev Adjusts state after TitanX tokens are unstaked.
* Only callable by authorized TitanStaker addresses.
* @param amountWithdrawn Amount of TitanX tokens unstaked.
* @notice Emits `TitanStakesEnded` event.
*/
function stakeEnded(uint256 amountWithdrawn) external onlyTitanStaker {
updateVault();
totalTitanUnstaked += amountWithdrawn;
emit TitanStakesEnded(_msgSender(), amountWithdrawn);
}
/**
* @notice Updates Titan Buy contract address.
* @dev Only owner can set a non-zero address.
* @param titanBuyAddress_ New contract address.
*/
function setTitanBuyContractAddress(address titanBuyAddress_) external onlyOwner {
if (titanBuyAddress_ == address(0)) revert InvalidAddress();
titanBuyAddress = titanBuyAddress_;
}
/**
* @notice Updates Wyvern Buy & Burn contract address.
* @dev Only owner can set a non-zero address.
* @param wyvernBuyAndBurnAddress_ New contract address.
*/
function setWyvernBuyAndBurnContractAddress(address wyvernBuyAndBurnAddress_) external onlyOwner {
if (wyvernBuyAndBurnAddress_ == address(0)) revert InvalidAddress();
wyvernBuyAndBurnAddress = wyvernBuyAndBurnAddress_;
}
/**
* @notice Refreshes vault's available TitanX balance
* @dev Calculates net holdings excluding genesis allocation
*/
function updateVault() public {
IERC20 titanX = IERC20(TITANX_ADDRESS);
uint256 balance = titanX.balanceOf(address(this));
vault = balance - genesisVault[address(titanX)];
}
/**
* @notice Queries available TitanX for new stakes
* @dev Returns net balance minus genesis allocation
*/
function getTotalTitanBalanceInVault() public view returns (uint256) {
return IERC20(TITANX_ADDRESS).balanceOf(address(this)) - genesisVault[address(IERC20(TITANX_ADDRESS))];
}
/**
* @notice Aggregates pending ETH rewards across all stakes
* @dev Scans all active stake contracts for pending rewards
*/
function totalPendingEthRewards() public view returns (uint256 pendingRewards) {
for (uint256 ids; ids < titanStakerContractsCount; ids++) {
TitanStaker titanStaker = TitanStaker(
payable(titanStakerContracts[ids])
);
pendingRewards += titanStaker.totalPendingEthRewards();
}
}
/**
* @notice Calculates total active TitanX stakes
* @dev Combines shares from all staker contracts
*/
function getCombinedWyvernActiveShares() public view returns (uint256) {
uint256 combinedActiveShares;
for (uint256 ids; ids < titanStakerContractsCount; ids++) {
combinedActiveShares += ITitanX(TITANX_ADDRESS).getUserCurrentActiveShares(titanStakerContracts[ids]);
}
return combinedActiveShares;
}
/**
* @notice Calculates the percentage of total Wyvern active shares in relation to global TitanX active shares.
* @return percentage The percentage of total Wyvern active shares relative to global TitanX active shares.
*/
function getWyvernActiveSharesPercentage() external view returns (uint256 percentage) {
uint256 combinedWyvernActiveShares = getCombinedWyvernActiveShares();
ITitanX titanX = ITitanX(TITANX_ADDRESS);
uint256 totalGlobalTitanXActiveShares = titanX.getGlobalActiveShares();
if (totalGlobalTitanXActiveShares == 0) {
return 0;
}
// Use a larger scaling factor first to prevent loss of precision
uint256 scaledShares = combinedWyvernActiveShares * SCALING_FACTOR_1e6;
// Then divide by totalGlobalTitanXActiveShares
percentage = (scaledShares / totalGlobalTitanXActiveShares);
return percentage;
}
/**
* @notice Allows the owner to withdraw assets from the Genesis Vault.
* @dev If `asset` is zero address, Ether is collected, else ERC20 tokens are transferred.
* @param asset Asset address to claim; zero address for Ether, non-zero for ERC20.
*/
function withdrawFromGenesis(address asset) external onlyOwner {
uint256 balance = genesisVault[asset];
genesisVault[asset] = 0;
require(balance > 0, "nothing to collect");
if (asset == address(0)) {
Address.sendValue(payable(owner()), balance);
} else {
IERC20 erc20 = IERC20(asset);
erc20.safeTransfer(owner(), balance);
}
}
/**
* @dev Computes the reward for executing a claim.
* @return reward The incentive reward.
*/
function callerRewardFeeForCallingClaim() external view returns (uint256 reward) {
reward = (totalPendingEthRewards() * REWARD_FEE) / PERCENTAGE_BASE_VAULT;
}
/**
* @notice Spawns new staker contract with deterministic address
* @dev Uses CREATE2 for deployment and updates registry
*/
function _createTitanStaker(bytes32 deploymentKey) private {
bytes memory bytecode = abi.encodePacked(type(TitanStaker).creationCode, abi.encode(address(this)));
uint256 stakerContractId = titanStakerContractsCount;
bytes32 salt = keccak256(
abi.encodePacked(address(this), stakerContractId, blockhash(block.number - 1), deploymentKey)
);
address precomputedAddress = Create2.computeAddress(salt, keccak256(bytecode), address(this));
require(_isAddressUnoccupied(precomputedAddress), "Address already exists");
address newTitanStakerContract = Create2.deploy(0, salt, bytecode);
require(newTitanStakerContract != address(0), "Deployment failed");
require(newTitanStakerContract == precomputedAddress, "Address mismatch");
activeTitanStakerContract = newTitanStakerContract;
titanStakerContracts[stakerContractId] = newTitanStakerContract;
ethDepositorWhitelist[newTitanStakerContract] = true;
titanStakerWhitelist[newTitanStakerContract] = true;
emit TitanStakerCreated(
stakerContractId,
newTitanStakerContract
);
titanStakerContractsCount += 1;
}
/**
* @notice Locks available TitanX in current stake contract
* @dev Transfers and initiates maximum duration stake
*/
function _startTitanXStake() private {
address activeTitanStakerContract_ = activeTitanStakerContract;
ITitanX titanX = ITitanX(TITANX_ADDRESS);
TitanStaker titanStaker = TitanStaker(
payable(activeTitanStakerContract_)
);
uint256 amountToStake = vault;
vault = 0;
titanX.safeTransfer(activeTitanStakerContract_, amountToStake);
titanStaker.stake();
totalTitanStaked += amountToStake;
emit TitanStakeStarted(activeTitanStakerContract_, amountToStake);
}
/**
* @notice Validates deployment address availability
* @dev Checks for existing contract bytecode
*/
function _isAddressUnoccupied(address addr) private view returns (bool) {
uint256 codeSize;
assembly {
codeSize := extcodesize(addr)
}
return codeSize == 0;
}
// NATIVE TOKEN HANDLING
/**
* @notice Processes incoming native token deposits
* Reverts if the sender is not whitelisted.
*/
receive() external payable {
require(ethDepositorWhitelist[msg.sender], "Sender unauthorized");
}
/**
* @notice Handles unexpected contract interactions
* @dev Blocks unintended native token transfers
*/
fallback() external {
revert("Fallback triggered");
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @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);
}
}// SPDX-License-Identifier: MIT
// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @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) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, 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.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @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));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @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);
}
}
}
}// SPDX-License-Identifier: MIT
// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
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);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
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);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
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();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// 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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Not enough balance for performing a CREATE2 deploy.
*/
error Create2InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev There's no code to deploy.
*/
error Create2EmptyBytecode();
/**
* @dev The deployment failed.
*/
error Create2FailedDeployment();
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
if (address(this).balance < amount) {
revert Create2InsufficientBalance(address(this).balance, amount);
}
if (bytecode.length == 0) {
revert Create2EmptyBytecode();
}
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
if (addr == address(0)) {
revert Create2FailedDeployment();
}
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := keccak256(start, 85)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: UNLICENSED
//██╗ ██╗██╗ ██╗██╗ ██╗███████╗██████╗ ███╗ ██╗██╗ ██╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗██╗ ████████╗ ██████╗ ███╗ ██╗ ████████╗██╗████████╗ █████╗ ███╗ ██╗██╗ ██╗
//██║ ██║╚██╗ ██╔╝██║ ██║██╔════╝██╔══██╗████╗ ██║╚██╗██╔╝ ██║██╔════╝ ██╔══██╗██║ ██║██║██║ ╚══██╔══╝ ██╔═══██╗████╗ ██║ ╚══██╔══╝██║╚══██╔══╝██╔══██╗████╗ ██║╚██╗██╔╝
//██║ █╗ ██║ ╚████╔╝ ██║ ██║█████╗ ██████╔╝██╔██╗ ██║ ╚███╔╝ ██║███████╗ ██████╔╝██║ ██║██║██║ ██║ ██║ ██║██╔██╗ ██║ ██║ ██║ ██║ ███████║██╔██╗ ██║ ╚███╔╝
//██║███╗██║ ╚██╔╝ ╚██╗ ██╔╝██╔══╝ ██╔══██╗██║╚██╗██║ ██╔██╗ ██║╚════██║ ██╔══██╗██║ ██║██║██║ ██║ ██║ ██║██║╚██╗██║ ██║ ██║ ██║ ██╔══██║██║╚██╗██║ ██╔██╗
//╚███╔███╔╝ ██║ ╚████╔╝ ███████╗██║ ██║██║ ╚████║██╔╝ ██╗ ██║███████║ ██████╔╝╚██████╔╝██║███████╗██║ ╚██████╔╝██║ ╚████║ ██║ ██║ ██║ ██║ ██║██║ ╚████║██╔╝ ██╗
//╚══╝╚══╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝
//██╗ ██╗██╗ ██╗██╗ ██╗███████╗██████╗ ███╗ ██╗██╗ ██╗ ██╗███████╗ ██╗ ██╗ ██████╗ ███████╗███████╗██╗██████╗ ██████╗
//██║ ██║╚██╗ ██╔╝██║ ██║██╔════╝██╔══██╗████╗ ██║╚██╗██╔╝ ██║██╔════╝ ████████╗██╔══██╗██╔════╝██╔════╝██║╚════██╗ ██╔═████╗
//██║ █╗ ██║ ╚████╔╝ ██║ ██║█████╗ ██████╔╝██╔██╗ ██║ ╚███╔╝ ██║███████╗ ╚██╔═██╔╝██║ ██║█████╗ █████╗ ██║ █████╔╝ ██║██╔██║
//██║███╗██║ ╚██╔╝ ╚██╗ ██╔╝██╔══╝ ██╔══██╗██║╚██╗██║ ██╔██╗ ██║╚════██║ ████████╗██║ ██║██╔══╝ ██╔══╝ ██║ ╚═══██╗ ████╔╝██║
//╚███╔███╔╝ ██║ ╚████╔╝ ███████╗██║ ██║██║ ╚████║██╔╝ ██╗ ██║███████║ ╚██╔═██╔╝██████╔╝███████╗██║ ██║██████╔╝██╗╚██████╔╝
//╚══╝╚══╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═════╝
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../interfaces/IWyvernOnBurn.sol";
import "../interfaces/IWyvernX.sol";
import "../interfaces/ITITANX.sol";
import "../libs/utilFunctions.sol";
import "../libs/TitanStaker.sol";
import "../libs/DifficultyLevel.sol";
import "../libs/HuntersGuild.sol";
import "../libs/Guardian.sol";
import "./WyvernVault.sol";
/** @title WyvernX is born */
contract WyvernX is ERC20, ReentrancyGuard, DifficultyLevel, HuntersGuild, Guardian {
using SafeERC20 for IERC20;
using SafeERC20 for ITitanX;
address private s_genesisAddress;
address private s_buyAndBurnAddress;
address private s_wyvernVaultAddress;
address private s_titanXAddress;
address private s_liquidityBondingAddress;
uint256 private s_undistributedLoot;
uint256 private s_claimingEnabledTimestamp;
/** @dev tracks if LP tokens have been minted */
WyvernManaPoolMinted private s_manaPoolMinted;
/** @dev tracks if claiming of WyvernX Hunters is already possible */
ClaimingEnabled private s_claimingFrozen;
/** @dev tracks if minting of WyvernX is no longer possible */
WyvernMintingPhaseFinished private s_mintingPhaseFinished;
event LootReceived(address indexed user, uint256 indexed day, uint256 indexed amount);
event TitanXLootDistributed(address indexed caller, uint256 indexed amount);
event CycleWarChestTriggered(
address indexed caller,
uint256 indexed cycleNo,
uint256 indexed reward
);
event WarChestLootClaimed(address indexed user, uint256 indexed reward);
error InsufficientBalance();
error GuildFeesTooLow();
error NotAllowed();
error NoCycleRewardToClaim();
error WarChestNotDepositedYet();
error EmptyUndistributeFees();
error InvalidBatchCount();
error MaxedWalletHunters();
error Wyvern_InvalidAddress();
error MintingPhaseFinished();
error InsufficientTitanXAllowance();
error InsufficientTitanXBalance();
error ClaimingFrozen();
/**
* @notice Constructor for the WyvernX ERC20 Token Contract.
* @param genesisAddress The address of the Genesis wallet address.
* @param wyvernVaultAddress The address of the WyvernX Vault contract.
*/
constructor(address genesisAddress, address wyvernVaultAddress) ERC20("WyvernX", "WyvernX") {
if (genesisAddress == address(0)) revert Wyvern_InvalidAddress();
if (wyvernVaultAddress == address(0)) revert Wyvern_InvalidAddress();
s_liquidityBondingAddress = LIQUIDITY_BONDING_ADDR;
s_genesisAddress = genesisAddress;
s_wyvernVaultAddress = wyvernVaultAddress;
s_manaPoolMinted = WyvernManaPoolMinted.NO;
s_mintingPhaseFinished = WyvernMintingPhaseFinished.NO;
}
/** @notice Recruit a new hunter to the guild
* @param strength Hunter strength from 1 to 100
* @param huntDays Hunt duration from 1 to 280 days
* @param amount TitanX tokens required for recruitment
*/
function hireHunter(
uint256 strength,
uint256 huntDays,
uint256 amount
) external nonReentrant increaseDifficultyLevel {
if (getUserLatestHunterId(_msgSender()) + 1 > MAX_HUNTERS_PER_WALLET) revert MaxedWalletHunters();
WyvernVault wyvernVault = WyvernVault(payable(s_wyvernVaultAddress));
// Get WyvernX current percentage of shares in TitanX
uint256 activeSharesPercentage = wyvernVault.getWyvernActiveSharesPercentage();
// prevent minting if minting phase is over
if (s_mintingPhaseFinished == WyvernMintingPhaseFinished.YES) revert MintingPhaseFinished();
if (activeSharesPercentage >= MINIMUM_SHAREPOOL_FOR_MINTING_PHASE) {
// finish minting phase
s_mintingPhaseFinished = WyvernMintingPhaseFinished.YES;
}
ITitanX titanX = ITitanX(TITANX_ADDRESS);
if (titanX.allowance(_msgSender(), address(this)) < amount) revert InsufficientTitanXAllowance();
if (titanX.balanceOf(_msgSender()) < amount) revert InsufficientTitanXBalance();
uint256 gStrength = getGlobalGuildStrength() + strength;
uint256 currentWRank = getGlobalWRank() + 1;
uint256 latestHunterCost = getLatestHunterCost();
uint256 batchHunterCost = getBatchHunterCost(strength, 1, latestHunterCost);
uint256 totalBeingLooted = getTotalWyvernBeingLooted() +
_hireHunter(
_msgSender(),
strength,
huntDays,
getCurrentLootableWyvern(),
getLatestHunterStrengthBonus(),
getCurrentEarlyHunterBonus(),
gStrength,
currentWRank,
batchHunterCost
);
_updateHunterStats(currentWRank, gStrength, totalBeingLooted);
_guildFees(strength, 1, amount);
}
/** @notice Recruit multiple hunters simultaneously
* @param strength Hunter strength from 1 to 100
* @param huntDays Hunt duration from 1 to 280 days
* @param count Number of hunters to recruit (1-100)
* @param amount TitanX tokens required for recruitment
*/
function batchHire(
uint256 strength,
uint256 huntDays,
uint256 count,
uint256 amount
) external payable nonReentrant increaseDifficultyLevel {
if (count == 0 || count > MAX_BATCH_HIRE_COUNT) revert InvalidBatchCount();
if (getUserLatestHunterId(_msgSender()) + count > MAX_HUNTERS_PER_WALLET) revert MaxedWalletHunters();
WyvernVault wyvernVault = WyvernVault(payable(s_wyvernVaultAddress));
// Get WyvernX current percentage of shares in TitanX
uint256 activeSharesPercentage = wyvernVault.getWyvernActiveSharesPercentage();
// prevent hiring Hunters if minting phase is over
if (s_mintingPhaseFinished == WyvernMintingPhaseFinished.YES) revert MintingPhaseFinished();
if (activeSharesPercentage >= MINIMUM_SHAREPOOL_FOR_MINTING_PHASE) {
// finish minting phase
s_mintingPhaseFinished = WyvernMintingPhaseFinished.YES;
}
ITitanX titanX = ITitanX(TITANX_ADDRESS);
if (titanX.allowance(_msgSender(), address(this)) < amount) revert InsufficientTitanXAllowance();
if (titanX.balanceOf(_msgSender()) < amount) revert InsufficientTitanXBalance();
_hireHunterSquad(
_msgSender(),
strength,
huntDays,
getCurrentLootableWyvern(),
getLatestHunterStrengthBonus(),
getCurrentEarlyHunterBonus(),
count,
getBatchHunterCost(strength, 1, getLatestHunterCost())
);
_guildFees(strength, count, amount);
}
/** @notice claim hunter
* @param id hunter id
*/
function claimHunter(uint256 id) external increaseDifficultyLevel nonReentrant {
if (!isClaimingEnabled()) revert ClaimingFrozen();
_mintReward(_claimHunter(_msgSender(), id, HunterAction.CLAIM));
}
/** @notice batch claim up to 100 hunters
*/
function batchClaimHunter() external increaseDifficultyLevel nonReentrant {
if (!isClaimingEnabled()) revert ClaimingFrozen();
_mintReward(_batchClaimHunters(_msgSender()));
}
/** @notice deposit WarChest
* @param amount Wyvern amount
* @param lockPeriod deposit lock Period
*/
function depositWarChest(uint256 amount, uint256 lockPeriod) external increaseDifficultyLevel nonReentrant {
if (balanceOf(_msgSender()) < amount) revert InsufficientBalance();
_burn(_msgSender(), amount);
_initFirstSharesCycleIndex(
_msgSender(),
_depositToWarChest(
_msgSender(),
amount,
lockPeriod,
getLatestInfluenceRate(),
getCurrentDay(),
getGlobalWarChestLootTriggered()
)
);
}
/** @notice withdraw warChest deposit
* @param id depositId
*/
function withdrawWarChest(uint256 id) external increaseDifficultyLevel nonReentrant {
_mint(
_msgSender(),
_withdrawWarChestDeposit(
_msgSender(),
id,
getCurrentDay(),
DepositAction.END,
getGlobalWarChestLootTriggered()
)
);
}
/** @notice distribute the collected guild loot into different parties/warChests */
function distributeLoot() external increaseDifficultyLevel nonReentrant {
(uint256 rewardFee, uint256 genesisWallet, uint256 vaultFunds, uint256 liquidityBondingFunds) = _distributeTitanX();
_sendFunds(rewardFee, genesisWallet, vaultFunds, liquidityBondingFunds);
}
/** @notice Activate War Chest loot distribution for cycle days 8, 28, 90, 369, 888
* As long as the cycle has met its maturity day (eg. Cycle28 is day 28), payout can be triggered in any day onwards
*/
function activateWarChest() external increaseDifficultyLevel nonReentrant {
uint256 protocolTotalInfluence = getTotalInfluence() - getTotalExpiredInfluence();
if (protocolTotalInfluence < 1) revert WarChestNotDepositedYet();
uint256 rewardFee;
uint256 genesisWallet;
uint256 vaultFunds;
uint256 liquidityBondingFunds;
if (s_undistributedLoot != 0)
(rewardFee, genesisWallet, vaultFunds, liquidityBondingFunds) = _distributeTitanX();
uint256 currentDay_ = getCurrentDay();
WarChestLootTriggered isTriggered = WarChestLootTriggered.NO;
_activateCycleWarChest(DAY8, protocolTotalInfluence, currentDay_) == WarChestLootTriggered.YES &&
isTriggered == WarChestLootTriggered.NO
? isTriggered = WarChestLootTriggered.YES
: isTriggered;
_activateCycleWarChest(DAY28, protocolTotalInfluence, currentDay_) == WarChestLootTriggered.YES &&
isTriggered == WarChestLootTriggered.NO
? isTriggered = WarChestLootTriggered.YES
: isTriggered;
_activateCycleWarChest(DAY90, protocolTotalInfluence, currentDay_) == WarChestLootTriggered.YES &&
isTriggered == WarChestLootTriggered.NO
? isTriggered = WarChestLootTriggered.YES
: isTriggered;
_activateCycleWarChest(DAY369, protocolTotalInfluence, currentDay_) ==
WarChestLootTriggered.YES &&
isTriggered == WarChestLootTriggered.NO
? isTriggered = WarChestLootTriggered.YES
: isTriggered;
_activateCycleWarChest(DAY888, protocolTotalInfluence, currentDay_) ==
WarChestLootTriggered.YES &&
isTriggered == WarChestLootTriggered.NO
? isTriggered = WarChestLootTriggered.YES
: isTriggered;
if (isTriggered == WarChestLootTriggered.YES) {
if (getGlobalWarChestLootTriggered() == WarChestLootTriggered.NO) _setGlobalWarChestLootTriggered();
}
if (rewardFee != 0) _sendFunds(rewardFee, genesisWallet, vaultFunds, liquidityBondingFunds);
}
/** @notice claim all user available TitanX WarChest loot */
function claimUserAvailableTitanWarChestLoot() external increaseDifficultyLevel nonReentrant {
uint256 reward = _claimCycleLoot(DAY8, CycleLootClaim.INFLUENCE);
reward += _claimCycleLoot(DAY28, CycleLootClaim.INFLUENCE);
reward += _claimCycleLoot(DAY90, CycleLootClaim.INFLUENCE);
reward += _claimCycleLoot(DAY369, CycleLootClaim.INFLUENCE);
reward += _claimCycleLoot(DAY888, CycleLootClaim.INFLUENCE);
if (reward == 0) revert NoCycleRewardToClaim();
IERC20 titanX = IERC20(TITANX_ADDRESS);
titanX.safeTransfer(_msgSender(), reward);
emit WarChestLootClaimed(_msgSender(), reward);
}
/** @notice Enable claiming of WyvernX Hunters on 281 WyvernX Contract Day
* Only owner can call this function
*/
function enableClaiming() external onlyOwner {
require(s_claimingFrozen == ClaimingEnabled.NO, "Claiming already enabled");
s_claimingFrozen = ClaimingEnabled.YES;
s_claimingEnabledTimestamp = block.timestamp;
}
/** @notice Set to new genesis wallet. Only genesis wallet can call this function
* @param newAddress new genesis wallet address
*/
function setNewGenesisAddress(address newAddress) external {
if (_msgSender() != s_genesisAddress) revert NotAllowed();
if (newAddress == address(0)) revert Wyvern_InvalidAddress();
s_genesisAddress = newAddress;
}
/** @notice Set BuyAndBurn Contract Address - able to change to new contract that supports UniswapV4+
* Only owner can call this function
* @param contractAddress BuyAndBurn contract address
*/
function setBuyAndBurnContractAddress(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert Wyvern_InvalidAddress();
s_buyAndBurnAddress = contractAddress;
}
/** @notice Set WyvernVault Contract Address
* Only owner can call this function
* @param contractAddress WyvernVault contract address
*/
function setWyvernVaultContractAddress(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert Wyvern_InvalidAddress();
s_wyvernVaultAddress = contractAddress;
}
/** @notice Set TitanX Contract Address
* Only owner can call this function
* @param contractAddress TitanX contract address
*/
function setTitanXContractAddress(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert Wyvern_InvalidAddress();
s_titanXAddress = contractAddress;
}
/** @notice Set Liquidity Bonding Address
* Only owner can call this function
* @param newAddress Liquidity Bonding Address
*/
function setLiquidityBondingAddress(address newAddress) external onlyOwner {
if (newAddress == address(0)) revert Wyvern_InvalidAddress();
s_liquidityBondingAddress = newAddress;
}
/**
* @notice Mints the initial liquidity for the WyvernX token.
* @dev This function mints a specified amount of tokens and sets up the TitanX staking phases.
* It can only be called once by the authorized Buy&Burn contract address.
* @param amount The amount of WyvernX tokens to be minted for initial liquidity.
*/
function mintInitialLiquidity(uint256 amount) external {
address buyAndBurnAddress_ = s_buyAndBurnAddress;
require(msg.sender == buyAndBurnAddress_, "must be B&B contract");
require(
s_manaPoolMinted == WyvernManaPoolMinted.NO,
"mana already minted"
);
_mint(buyAndBurnAddress_, amount);
s_manaPoolMinted = WyvernManaPoolMinted.YES;
}
/** @notice burn all BuyAndBurn contract WyvernX tokens */
function burnLPTokens() external increaseDifficultyLevel {
_burn(s_buyAndBurnAddress, balanceOf(s_buyAndBurnAddress));
}
/** @notice Returns if claiming is enabled
* @return true if claiming is enabled, false otherwise
*/
function isClaimingEnabled() public view returns (bool) {
return s_claimingFrozen == ClaimingEnabled.YES || getCurrentDay() >= AUTO_ENABLE_MINT_CLAIMING_DAY;
}
/** @notice Returns the start time of the claiming period
* @return start time of the WyvernX Hunters claiming period
*/
function getClaimingStartTime() external view returns (uint256) {
return s_claimingEnabledTimestamp;
}
// -----------------------------------------
// Private functions
// -----------------------------------------
/** @dev mint reward to user and 3% to genesis wallet
* @param reward WyvernX amount
*/
function _mintReward(uint256 reward) private {
_mint(_msgSender(), reward);
_mint(s_genesisAddress, (reward * 300) / PERCENT_BPS);
}
/** @dev send TitanX fess to respective parties
* @param incentiveFee fees for caller to run distributeFees()
* @param genesisWalletFunds funds for genesis wallet
* @param vaultFunds funds for TitanX vault inside of Wyvern
* @param liquidityBondingFunds funds for liquidity bonding
*/
function _sendFunds(
uint256 incentiveFee,
uint256 genesisWalletFunds,
uint256 vaultFunds,
uint256 liquidityBondingFunds
) private {
IERC20 titanX = IERC20(TITANX_ADDRESS);
titanX.safeTransfer(_msgSender(), incentiveFee);
titanX.safeTransfer(s_genesisAddress, genesisWalletFunds);
titanX.safeTransfer(s_liquidityBondingAddress, liquidityBondingFunds);
// stop sending TitanX to Vault if minting phase is over
if (s_mintingPhaseFinished != WyvernMintingPhaseFinished.YES) {
titanX.safeTransfer(s_wyvernVaultAddress, vaultFunds);
}
}
/** @dev calculation to distribute collected protocol fees into different pools/parties */
function _distributeTitanX()
private
returns (uint256 incentiveFee, uint256 genesisWallet, uint256 vaultFunds, uint256 liquidityBondingFunds)
{
updateUndistributedLoot();
uint256 accumulatedFees = s_undistributedLoot;
if (accumulatedFees == 0) revert EmptyUndistributeFees();
s_undistributedLoot = 0;
emit TitanXLootDistributed(_msgSender(), accumulatedFees);
incentiveFee = (accumulatedFees * REWARD_FEE_PERCENT) / REWARD_FEE_PERCENTAGE_BASE_WYVERNX; //3%
accumulatedFees -= incentiveFee;
vaultFunds = 0;
// Only calculate and distribute TitanX to the Vault if minting phase has not ended
if (s_mintingPhaseFinished != WyvernMintingPhaseFinished.YES) {
vaultFunds = (accumulatedFees * PERCENT_TO_WYVERN_VAULT) / PERCENT_BPS;
}
genesisWallet = (accumulatedFees * PERCENT_TO_GENESIS) / PERCENT_BPS;
liquidityBondingFunds = (accumulatedFees * PERCENT_TO_LIQUIDITY_BONDING) / PERCENT_BPS;
uint256 cycleRewardPool = accumulatedFees - genesisWallet - vaultFunds - liquidityBondingFunds;
//cycle payout
if (cycleRewardPool != 0) {
uint256 cycle8Reward = (cycleRewardPool * CYCLE_8_PERCENT) / PERCENT_BPS;
uint256 cycle28Reward = (cycleRewardPool * CYCLE_28_PERCENT) / PERCENT_BPS;
uint256 cycle90Reward = (cycleRewardPool * CYCLE_90_PERCENT) / PERCENT_BPS;
uint256 cycle369Reward = (cycleRewardPool * CYCLE_369_PERCENT) / PERCENT_BPS;
_setCycleWarChest(DAY8, cycle8Reward);
_setCycleWarChest(DAY28, cycle28Reward);
_setCycleWarChest(DAY90, cycle90Reward);
_setCycleWarChest(DAY369, cycle369Reward);
_setCycleWarChest(
DAY888,
cycleRewardPool - cycle8Reward - cycle28Reward - cycle90Reward - cycle369Reward
);
}
}
/** @dev calculate required guild fees, and return the balance
* @param strength strength 1-100
* @param count how many hunters
*/
function _guildFees(uint256 strength, uint256 count, uint256 amount) private {
uint256 guildFee = getBatchHunterCost(strength, count, getLatestHunterCost());
if (amount < guildFee) revert GuildFeesTooLow();
ITitanX titanX = ITitanX(TITANX_ADDRESS);
titanX.safeTransferFrom(_msgSender(), address(this), amount);
s_undistributedLoot += uint256(amount);
emit LootReceived(_msgSender(), getCurrentDay(), amount);
}
/** @dev calculate loot for each cycle day
* @param cycleNo cycle day 8, 28, 90, 369, 888
* @param protocolTotalInfluence total amount of active influence
* @param currentDay current day
* @return triggered is WarChest Loot Triggered
*/
function _activateCycleWarChest(
uint256 cycleNo,
uint256 protocolTotalInfluence,
uint256 currentDay
) private returns (WarChestLootTriggered triggered) {
if (currentDay < getNextCycleLootDay(cycleNo)) return WarChestLootTriggered.NO;
_setNextCycleWarChestDay(cycleNo);
uint256 reward = getCycleLootWarChest(cycleNo);
if (reward == 0) return WarChestLootTriggered.NO;
_calcCycleLootPerInfluence(cycleNo, reward, protocolTotalInfluence);
emit CycleWarChestTriggered(_msgSender(), cycleNo, reward);
return WarChestLootTriggered.YES;
}
/** @dev calculate user's loot with specified cycle day and and update user's last claim cycle index
* @param cycleNo cycle day 8, 28, 90, 369, 888
* @param cycleLootClaim claim type - (Influence=0)
*/
function _claimCycleLoot(uint256 cycleNo, CycleLootClaim cycleLootClaim) private returns (uint256) {
(
uint256 reward,
uint256 userClaimCycleIndex,
uint256 userClaimInfluenceIndex
) = _calculateUserCycleReward(_msgSender(), cycleNo, cycleLootClaim);
if (cycleLootClaim == CycleLootClaim.INFLUENCE)
_updateUserClaimIndexes(
_msgSender(),
cycleNo,
userClaimCycleIndex,
userClaimInfluenceIndex
);
return reward;
}
/** @dev calculate user loot with specified cycle day and claim type.
* @param user user address
* @param cycleNo cycle day 8, 28, 90, 369, 888
* @param cycleLootClaim claim type
* @return rewards calculated reward
* @return userClaimCycleIndex last claim cycle index
* @return userClaimInfluenceIndex last Claim Influence Index
*/
function _calculateUserCycleReward(address user, uint256 cycleNo, CycleLootClaim cycleLootClaim)
private
view
returns (
uint256 rewards,
uint256 userClaimCycleIndex,
uint256 userClaimInfluenceIndex
)
{
uint256 cycleMaxIndex = getCurrentCycleLootIndex(cycleNo);
if (cycleLootClaim == CycleLootClaim.INFLUENCE) {
(userClaimCycleIndex, userClaimInfluenceIndex) = getUserLastClaimIndex(user, cycleNo);
uint256 influenceMaxIndex = getUserCurrentInfluenceIndex(user);
for (uint256 i = userClaimCycleIndex; i <= cycleMaxIndex; i++) {
(uint256 lootPerInfluence, uint256 lootDay) = getLootPerInfluence(cycleNo, i);
uint256 influence;
for (uint256 j = userClaimInfluenceIndex; j <= influenceMaxIndex; j++) {
if (getUserInfluenceDayFromInfluenceIndex(user, j) <= lootDay)
influence = getUserInfluenceAtIndex(user, j);
else break;
userClaimInfluenceIndex = j;
}
if (lootPerInfluence != 0 && influence != 0) {
rewards += (influence * lootPerInfluence) / SCALING_FACTOR_1e18;
}
userClaimCycleIndex = i + 1;
}
}
}
/**
* @notice Updates the undistributed loot balance to match the current TITANX token balance.
* @dev Refreshes s_undistributedLoot by querying the contract's TITANX holdings.
*/
function updateUndistributedLoot() public {
IERC20 titanX = IERC20(TITANX_ADDRESS);
uint256 balance = titanX.balanceOf(address(this));
s_undistributedLoot = balance;
}
/** @notice get contract TitanX balance
* @return balance TitanX balance
*/
function getTitanXBalance() public view returns (uint256) {
IERC20 titanX = IERC20(TITANX_ADDRESS);
return titanX.balanceOf(address(this));
}
/** @notice get undistributed loot balance
* @return amount of loot fees that were generated from hunters and TitanX staking rewards
*/
function getUndistributedLoot() public view returns (uint256) {
return s_undistributedLoot;
}
/** @notice get user TitanX payout for all cycles
* @param user user address
* @return reward total reward
*/
function getUserTitanClaimableTotal(address user) public view returns (uint256 reward) {
uint256 _reward;
(_reward, , ) = _calculateUserCycleReward(user, DAY8, CycleLootClaim.INFLUENCE);
reward += _reward;
(_reward, , ) = _calculateUserCycleReward(user, DAY28, CycleLootClaim.INFLUENCE);
reward += _reward;
(_reward, , ) = _calculateUserCycleReward(user, DAY90, CycleLootClaim.INFLUENCE);
reward += _reward;
(_reward, , ) = _calculateUserCycleReward(user, DAY369, CycleLootClaim.INFLUENCE);
reward += _reward;
(_reward, , ) = _calculateUserCycleReward(user, DAY888, CycleLootClaim.INFLUENCE);
reward += _reward;
}
/** @notice Public function to return the total minted WyvernX
* @return total amount of WyvernX minted
*/
function getTotalWyvernRewardsMinted() public view returns (uint256) {
return getTotalLootedWyvern();
}
//Public function for updating difficulty level
/** @notice allow anyone to sync increaseDifficultyLevel manually */
function manualIncreaseLevelDifficulty() public increaseDifficultyLevel {}
/** @notice Public function to return the current Vault address
* @return vault address
*/
function getWyvernVaultAddress() public view returns (address) {
return s_wyvernVaultAddress;
}
/** @notice Public function to return the current status of minting
* @return minting status 0 or 1
*/
function getIsMintingPhaseFinished() public view returns (WyvernMintingPhaseFinished) {
return s_mintingPhaseFinished;
}
// NATIVE TOKEN HANDLING
/**
* @dev Receive function to handle plain Ether transfers.
* Reverts on all ETH deposits as WyvernX doesn't utilize ETH in its core functionality.
*/
receive() external payable {
revert("ETH not accepted");
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
enum StakeStatus {
ACTIVE,
ENDED
}
struct UserStakeInfo {
uint152 titanAmount;
uint128 shares;
uint16 numOfDays;
uint48 stakeStartTs;
uint48 maturityTs;
StakeStatus status;
}
struct UserStake {
uint256 sId;
uint256 globalStakeId;
UserStakeInfo stakeInfo;
}
interface IStakeInfo {
function getUserStakes(address user) external view returns (UserStake[] memory);
function getUserStakeInfo(address user, uint256 id) external view returns (UserStakeInfo memory);
function getGlobalActiveShares() external view returns (uint256);
function getUserCurrentActiveShares(address user) external view returns (uint256);
}
/**
* @title The TitanX interface used by WyvernX to manage stakes
*/
interface ITitanX is IERC20, IStakeInfo {
function startStake(uint256 amount, uint256 numOfDays) external;
function claimUserAvailableETHPayouts() external;
function getUserETHClaimableTotal(address user) external view returns (uint256 reward);
function manualDailyUpdate() external;
function triggerPayouts() external;
function startMint(uint256 mintPower, uint256 numOfDays) external payable;
function batchMint(uint256 mintPower, uint256 numOfDays, uint256 count) external payable;
function getCurrentMintCost() external view returns (uint256);
function endStake(uint256 id) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
interface IWyvernOnBurn {
function onBurn(address user, uint256 amount) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
interface IWyvernX {
function balanceOf(address account) external view returns (uint256);
function getBalance() external;
function mintLPTokens() external;
function burnLPTokens() external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "../libs/enum.sol";
import "../libs/constant.sol";
abstract contract DifficultyLevel {
uint256 private immutable wyvernBirthTimestamp;
uint256 private currentDay;
uint256 private latestInfluenceRate;
uint256 private latestHunterCost;
/** @dev Lootable Wyvern starts at 150 ether, decreases to 10 ether. Stops when WyvernX has 41% of TitanX Staking Share Pool. */
uint256 private currentLootableWyvern;
uint256 private latestHunterStrengthBonus;
uint256 private currentEHBonus;
/** @dev Tracks successful WarChestLoot for cycle days 8, 28, 90, 369, 888. Used in end stake for influence tracking. */
WarChestLootTriggered private isGlobalWarChestLootTriggered;
/** @dev Maps cycle days to loot amounts. */
mapping(uint256 => uint256) private cycleLoot;
/** @dev Maps cycle days to loot indices, incremented on successful WarChestLoot. */
mapping(uint256 => uint256) private cycleLootIndex;
/** @dev Stores loot info (day and loot per influence) for each cycle day. */
mapping(uint256 => mapping(uint256 => CycleLootPerInfluence)) private cycleLootPerInfluence;
/** @dev Tracks user's last claimed influence index for cycle and WarChestLoot. */
mapping(address => mapping(uint256 => UserCycleClaimIndex)) private addressCycleToLastLootClaimIndex;
/** @dev Maps cycle days to next WarChestLoot days. */
mapping(uint256 => uint256) nextCycleLootDay;
struct CycleLootPerInfluence {
uint256 day;
uint256 lootPerInfluence;
}
struct UserCycleClaimIndex {
uint256 cycleIndex;
uint256 influenceIndex;
}
event DifficultyLevelIncreasedStats(
uint256 indexed day,
uint256 indexed hunterCost,
uint256 indexed influenceRate,
uint256 lootableWyvern,
uint256 mintPowerBonus,
uint256 EHBonus
);
/** @dev Updates daily variables for all external/public functions (excluding view). */
modifier increaseDifficultyLevel() {
_increaseDifficultyLevel();
_;
}
constructor() {
wyvernBirthTimestamp = block.timestamp;
currentDay = 1;
latestHunterCost = STARTING_MAX_HUNTER_COST;
currentLootableWyvern = STARTING_MAX_DAILY_LOOTABLE_WYVERN;
latestInfluenceRate = STARTING_INFLUENCE_RATE;
latestHunterStrengthBonus = STARTING_HUNTER_STRENGTH_BONUS;
currentEHBonus = STARTING_EH_BONUS;
nextCycleLootDay[DAY8] = DAY8;
nextCycleLootDay[DAY28] = DAY28;
nextCycleLootDay[DAY90] = DAY90;
nextCycleLootDay[DAY369] = DAY369;
nextCycleLootDay[DAY888] = DAY888;
}
/** @dev Calculates and updates daily variables, resets triggers. */
function _increaseDifficultyLevel() private {
uint256 currentDay_ = currentDay;
uint256 currentBlockDay = ((block.timestamp - wyvernBirthTimestamp) / 1 days) + 1;
if (currentBlockDay > currentDay_) {
uint256 newHunterCost = latestHunterCost;
uint256 newInfluenceRate = latestInfluenceRate;
uint256 newLootableWyvern = currentLootableWyvern;
uint256 newHunterStrengthBonus = latestHunterStrengthBonus;
uint256 newEHBonus = currentEHBonus;
uint256 dayDifference = currentBlockDay - currentDay_;
for (uint256 i; i < dayDifference; i++) {
newHunterCost = (newHunterCost * DAILY_HUNTER_COST_INCREASE) / PERCENT_BPS;
// Freeze influence rate reduction for the first 281 days
if (currentDay_ >= MINTING_FREEZE_PERIOD) {
newInfluenceRate = (newInfluenceRate * DAILY_SHARE_RATE_INCREASE_STEP) / PERCENT_BPS;
}
newLootableWyvern =
(newLootableWyvern * DAILY_LOOTABLE_WYVERN_REDUCTION) /
PERCENT_BPS;
newHunterStrengthBonus =
(newHunterStrengthBonus * DAILY_MINTPOWER_INCREASE_BONUS_REDUCTION) /
PERCENT_BPS;
if (newHunterCost > CAPPED_MAX_HUNTER_COST) {
newHunterCost = CAPPED_MAX_HUNTER_COST;
}
if (newInfluenceRate > CAPPED_MAX_RATE) newInfluenceRate = CAPPED_MAX_RATE;
if (newLootableWyvern < CAPPED_MIN_DAILY_LOOTABLE_WYVERN) {
newLootableWyvern = CAPPED_MIN_DAILY_LOOTABLE_WYVERN;
}
if (newHunterStrengthBonus < MIN_HUNTER_STRENGTH_BONUS) {
newHunterStrengthBonus = MIN_HUNTER_STRENGTH_BONUS;
}
if (currentBlockDay <= MAX_BONUS_DAY) {
newEHBonus -= EH_BONUS_FIXED_REDUCTION_PER_DAY;
} else {
newEHBonus = EH_BONUS_END;
}
emit DifficultyLevelIncreasedStats(
++currentDay_,
newHunterCost,
newInfluenceRate,
newLootableWyvern,
newHunterStrengthBonus,
newEHBonus
);
}
latestHunterCost = newHunterCost;
latestInfluenceRate = newInfluenceRate;
currentLootableWyvern = newLootableWyvern;
latestHunterStrengthBonus = newHunterStrengthBonus;
currentEHBonus = newEHBonus;
currentDay = currentBlockDay;
isGlobalWarChestLootTriggered = WarChestLootTriggered.NO;
}
}
/** @dev Initializes first influence from last claim index + 1. */
function _initFirstSharesCycleIndex(address user, uint256 isFirstInfluece) internal {
if (isFirstInfluece == 1) {
if (cycleLootIndex[DAY8] != 0) {
addressCycleToLastLootClaimIndex[user][DAY8].cycleIndex = cycleLootIndex[DAY8] + 1;
addressCycleToLastLootClaimIndex[user][DAY28].cycleIndex = cycleLootIndex[DAY28] + 1;
addressCycleToLastLootClaimIndex[user][DAY90].cycleIndex = cycleLootIndex[DAY90] + 1;
addressCycleToLastLootClaimIndex[user][DAY369].cycleIndex = cycleLootIndex[DAY369] + 1;
addressCycleToLastLootClaimIndex[user][DAY888].cycleIndex = cycleLootIndex[DAY888] + 1;
}
}
}
/** @dev Calculates loot per influence for specified cycle. */
function _calcCycleLootPerInfluence(
uint256 cycleNo,
uint256 loot,
uint256 protocolTotalInfluence
) internal returns (uint256 index) {
cycleLoot[cycleNo] = 0;
index = ++cycleLootIndex[cycleNo];
cycleLootPerInfluence[cycleNo][index].lootPerInfluence = (loot * SCALING_FACTOR_1e18) / protocolTotalInfluence;
cycleLootPerInfluence[cycleNo][index].day = getCurrentDay();
}
/** @dev Updates stats after WarChestLoot is claimed. */
function _updateUserClaimIndexes(
address user,
uint256 cycleNo,
uint256 userClaimCycleIndex,
uint256 userClaimInfluenceIndex
) internal {
if (userClaimCycleIndex != addressCycleToLastLootClaimIndex[user][cycleNo].cycleIndex)
addressCycleToLastLootClaimIndex[user][cycleNo].cycleIndex = userClaimCycleIndex;
if (userClaimInfluenceIndex != addressCycleToLastLootClaimIndex[user][cycleNo].influenceIndex)
addressCycleToLastLootClaimIndex[user][cycleNo].influenceIndex = userClaimInfluenceIndex;
}
/** @dev Marks loot as triggered for cycle days. */
function _setGlobalWarChestLootTriggered() internal {
isGlobalWarChestLootTriggered = WarChestLootTriggered.YES;
}
/** @dev Adds reward to specified cycle's war chest. */
function _setCycleWarChest(uint256 cycleNo, uint256 reward) internal {
cycleLoot[cycleNo] += reward;
}
/** @dev Updates next WarChestLoot day for specified cycle. */
function _setNextCycleWarChestDay(uint256 cycleNo) internal {
uint256 lootDay = nextCycleLootDay[cycleNo];
uint256 currentDay_ = currentDay;
if (currentDay_ >= lootDay) {
nextCycleLootDay[cycleNo] +=
cycleNo *
(((currentDay_ - lootDay) / cycleNo) + 1);
}
}
/** Views */
/** @notice Returns current block timestamp. */
function getCurrentBlockTimeStamp() public view returns (uint256) {
return block.timestamp;
}
/** @notice Returns current day. */
function getCurrentDay() public view returns (uint256) {
return currentDay;
}
/** @notice Returns latest Hunter Cost. */
function getLatestHunterCost() public view returns (uint256) {
return latestHunterCost;
}
/** @notice Returns latest Influence Rate. */
function getLatestInfluenceRate() public view returns (uint256) {
return latestInfluenceRate;
}
/** @notice Returns current Lootable Wyvern. */
function getCurrentLootableWyvern() public view returns (uint256) {
return currentLootableWyvern;
}
/** @notice Returns latest Hunter Strength Bonus. */
function getLatestHunterStrengthBonus() public view returns (uint256) {
return latestHunterStrengthBonus;
}
/** @notice Returns current Early Hunter Bonus. */
function getCurrentEarlyHunterBonus() public view returns (uint256) {
return currentEHBonus;
}
/** @notice Returns current cycle loot index for specified cycle day. */
function getCurrentCycleLootIndex(uint256 cycleNo) public view returns (uint256) {
return cycleLootIndex[cycleNo];
}
/** @notice Checks if war chest has been triggered. */
function getGlobalWarChestLootTriggered() public view returns (WarChestLootTriggered) {
return isGlobalWarChestLootTriggered;
}
/** @notice Returns war chest reward for specified cycle day. */
function getCycleLootWarChest(uint256 cycleNo) public view returns (uint256) {
return cycleLoot[cycleNo];
}
/** @notice Returns loot per influence and day for specified cycle day and index. */
function getLootPerInfluence(uint256 cycleNo, uint256 index) public view returns (uint256, uint256) {
return (
cycleLootPerInfluence[cycleNo][index].lootPerInfluence,
cycleLootPerInfluence[cycleNo][index].day
);
}
/** @notice Returns user's last claimed influence indexes for specified cycle day. */
function getUserLastClaimIndex(
address user,
uint256 cycleNo
) public view returns (uint256 cycleIndex, uint256 influenceIndex) {
return (
addressCycleToLastLootClaimIndex[user][cycleNo].cycleIndex,
addressCycleToLastLootClaimIndex[user][cycleNo].influenceIndex
);
}
/** @notice Returns day when WyvernX was born. */
function getWyvernBirthTimestamp() public view returns (uint256) {
return wyvernBirthTimestamp;
}
/** @notice Returns next Cycle Loot Day. */
function getNextCycleLootDay(uint256 cycleNo) public view returns (uint256) {
return nextCycleLootDay[cycleNo];
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/Context.sol";
error IsNotOnwer();
abstract contract Guardian is Context {
address private s_owner;
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
s_owner = _msgSender();
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (s_owner != _msgSender()) revert IsNotOnwer();
}
/**
* @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 onlyOwner {
_setOwner(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 onlyOwner {
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
s_owner = newOwner;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "../libs/utilFunctions.sol";
import "../libs/enum.sol";
abstract contract HuntersGuild {
/** @dev track guild wyvernRank */
uint256 private globalWRank;
/** @dev track total amount of claimed Hunters */
uint256 private totalHuntersClaimed;
/** @dev track total WyvernX looted by Hunters */
uint256 private totalLootedWyvern;
/** @dev track total WyvernX being looted currently from Hunters */
uint256 private totalWyvernBeingLooted;
/** @dev track guild strength */
uint256 private globalGuildStrength;
/** @dev track guild's depositId */
uint256 private currentDepositId;
/** @dev track guild's influence */
uint256 private totalInfluence;
/** @dev track guild's expired influence */
uint256 private totalExpiredInfluence;
/** @dev track guild's Wyvern in war chest */
uint256 private totalWyvernInWarChests;
/** @dev track guild's withdrawal penalties */
uint256 private totalWithdrawalPenalties;
/** @dev track guild's withdrawn deposits */
uint256 private totalWithdrawnDeposits;
/** @dev track address => hunterId */
mapping(address => uint256) private addressHId;
/** @dev track address, hunterId => WyvernRank */
mapping(address => mapping(uint256 => WyvernRank)) private addressHIdToWyvernRank;
/** @dev track guild wRank => Hunter */
mapping(uint256 => Hunter) private wRankToHunter;
/** @dev track address => depositId */
mapping(address => uint256) private addressDepositId;
/** @dev track address, depositId => global depositId */
mapping(address => mapping(uint256 => uint256)) private addressDepositIdToGlobalDepositId;
/** @dev track global deposit Id => WarChestDeposit */
mapping(uint256 => WarChestDeposit) private globalDepositIdToWarChestDeposit;
/** @dev track address => tracks the latest index of influence share for each user */
mapping(address => uint256) private userInfluenceIndex;
/** @dev track user total current influence by day */
mapping(address => mapping(uint256 => UserInfluence)) private addressIdToCurrentInfluence;
struct Hunter {
uint256 strength;
uint16 huntDays;
uint256 lootableWyvern;
uint48 huntStart;
uint48 huntEnd;
uint256 hunterStrengthBonus;
uint256 EHBonus;
uint256 lootedWyvern;
uint256 hunterCost;
HunterStatus status;
}
struct UserHunter {
uint256 hunterId;
uint256 wRank;
uint256 guildStrength;
Hunter hunter;
}
struct WyvernRank {
uint256 wRank;
uint256 guildStrength;
}
struct WarChestDeposit {
uint256 wyvernAmount;
uint256 influence;
uint16 lockPeriod;
uint48 depositTimestamp;
uint48 unlockTimestamp;
DepositStatus status;
}
struct UserWarChest {
uint256 depositId;
uint256 currentDepositId;
WarChestDeposit warChestDeposit;
}
struct UserInfluence {
uint256 day;
uint256 currentInfluence;
}
event HunterStarted(
address indexed user,
uint256 indexed wRank,
uint256 indexed guildStrength,
Hunter hunter
);
event HunterClaimed(
address indexed user,
uint256 indexed wRank,
uint256 wyvernLooted
);
event WarChestDeposited(
address indexed user,
uint256 indexed currentDepositId,
uint256 lockPeriod,
WarChestDeposit indexed warChestDeposit
);
event WarChestWithdrawn(
address indexed user,
uint256 indexed currentDepositId,
uint256 wyvernAmount,
uint256 indexed penalty,
uint256 penaltyAmount
);
error InvalidHuntLength();
error InvalidHunterStrength();
error NoSuchHunterExists();
error HunterAlreadyClaimed();
error HuntNotFinished();
error InvalidLockPeriod();
error YouHaveNoInfluence();
error NoDeposits();
error DepositAlreadyWithdrawn();
error MaxDepositsReached();
/** @dev hire a new hunter
* @param user user address
* @param strength strength of hunter
* @param huntDays length of hunt
* @param lootableWyvern lootable WyvernX
* @param hunterStrengthBonus hunter strength bonus
* @param EHBonus Early Hunter Bonus
* @param guildStrength guild Strength
* @param currentWRank current global wRank
* @param hunterCost cost to hire a hunter
*/
function _hireHunter(
address user,
uint256 strength,
uint256 huntDays,
uint256 lootableWyvern,
uint256 hunterStrengthBonus,
uint256 EHBonus,
uint256 guildStrength,
uint256 currentWRank,
uint256 hunterCost
) internal returns (uint256 lootable) {
if (huntDays == 0 || huntDays > MAX_HUNT_LENGTH) revert InvalidHuntLength();
if (strength == 0 || strength > MAX_HUNTER_STRENGTH_CAP) revert InvalidHunterStrength();
lootable = calculateHunterReward(strength, huntDays, lootableWyvern, EHBonus);
Hunter memory hunter = Hunter({
strength: strength,
huntDays: uint16(huntDays),
lootableWyvern: lootable,
hunterStrengthBonus: hunterStrengthBonus,
EHBonus: EHBonus,
huntStart: uint48(block.timestamp),
huntEnd: uint48(block.timestamp + (huntDays * SECONDS_IN_DAY)),
lootedWyvern: 0,
hunterCost: hunterCost,
status: HunterStatus.ACTIVE
});
uint256 id = ++addressHId[user];
addressHIdToWyvernRank[user][id].wRank = currentWRank;
addressHIdToWyvernRank[user][id].guildStrength = guildStrength;
wRankToHunter[currentWRank] = hunter;
emit HunterStarted(user, currentWRank, guildStrength, hunter);
}
/** @dev hire a squad of hunters (up to max 100 hunters with same hunt duration)
* @param user user address
* @param strength strength of hunter
* @param huntDays length of hunt
* @param lootableWyvern lootable WyvernX
* @param hunterStrengthBonus hunter strength bonus
* @param EHBonus Early Hunter Bonus
* @param squadSize count of hunters
* @param hunterCost cost to hire a hunter
*/
function _hireHunterSquad(
address user,
uint256 strength,
uint256 huntDays,
uint256 lootableWyvern,
uint256 hunterStrengthBonus,
uint256 EHBonus,
uint256 squadSize,
uint256 hunterCost
) internal {
uint256 guildStrength = globalGuildStrength;
uint256 currentWRank = globalWRank;
uint256 totalLooting = totalWyvernBeingLooted;
for (uint256 i = 0; i < squadSize; i++) {
guildStrength += strength;
totalLooting += _hireHunter(
user,
strength,
huntDays,
lootableWyvern,
hunterStrengthBonus,
EHBonus,
guildStrength,
++currentWRank,
hunterCost
);
}
_updateHunterStats(currentWRank, guildStrength, totalLooting);
}
/** @dev calculate loot for hunter claim
* @param user user address
* @param id hunter id
* @param action claim hunter
* @return loot calculated final Wyvern loot
*/
function _claimHunter(
address user,
uint256 id,
HunterAction action
) internal returns (uint256 loot) {
uint256 wRank = addressHIdToWyvernRank[user][id].wRank;
uint256 guildStrength = addressHIdToWyvernRank[user][id].guildStrength;
if (wRank == 0) revert NoSuchHunterExists();
Hunter memory hunter = wRankToHunter[wRank];
if (hunter.status == HunterStatus.CLAIMED) revert HunterAlreadyClaimed();
if (hunter.huntEnd > block.timestamp && action == HunterAction.CLAIM)
revert HuntNotFinished();
totalWyvernBeingLooted -= hunter.lootableWyvern;
totalLootedWyvern += hunter.lootableWyvern;
loot = _calculateClaimLoot(user, wRank, guildStrength, hunter, action);
}
/** @dev calculate loot up to 100 claims
* @param user user address
* @return loot total loot from batch hunter claim
*/
function _batchClaimHunters(address user) internal returns (uint256 loot) {
uint256 maxId = addressHId[user];
uint256 claimCount;
uint256 wRank;
uint256 wyvernLooting;
Hunter memory mint;
for (uint256 i = 1; i <= maxId; i++) {
wRank = addressHIdToWyvernRank[user][i].wRank;
mint = wRankToHunter[wRank];
if (mint.status == HunterStatus.ACTIVE && block.timestamp >= mint.huntEnd) {
loot += _calculateClaimLoot(
user,
wRank,
addressHIdToWyvernRank[user][i].guildStrength,
mint,
HunterAction.CLAIM
);
wyvernLooting += mint.lootableWyvern;
++claimCount;
}
if (claimCount == 100) break;
}
totalWyvernBeingLooted -= wyvernLooting;
totalLootedWyvern += wyvernLooting;
}
/** @dev create a new war chest deposit
* @param user user address
* @param amount WyvernX amount
* @param lockPeriod deposit lock duration
* @param influenceRate current influence rate
* @param day current contract day
* @param isWarChestTriggered has global payout triggered
* @return startingInfluence protocol's first influence
*/
function _depositToWarChest(
address user,
uint256 amount,
uint256 lockPeriod,
uint256 influenceRate,
uint256 day,
WarChestLootTriggered isWarChestTriggered
) internal returns (uint256 startingInfluence) {
uint256 depositId = ++addressDepositId[user];
if (depositId > MAX_DEPOSITS_PER_WALLET) revert MaxDepositsReached();
if (lockPeriod < MIN_LOCK_PERIOD || lockPeriod > MAX_LOCK_PERIOD)
revert InvalidLockPeriod();
uint256 influence = calculateInfluence(amount, lockPeriod, influenceRate);
if (influence / SCALING_FACTOR_1e18 < 1) revert YouHaveNoInfluence();
uint256 currentDepositId_ = ++currentDepositId;
uint256 unlockTimestamp;
unlockTimestamp = block.timestamp + (lockPeriod * SECONDS_IN_DAY);
WarChestDeposit memory warChestDeposit = WarChestDeposit({
wyvernAmount: amount,
influence: influence,
lockPeriod: uint16(lockPeriod),
depositTimestamp: uint48(block.timestamp),
unlockTimestamp: uint48(unlockTimestamp),
status: DepositStatus.ACTIVE
});
addressDepositIdToGlobalDepositId[user][depositId] = currentDepositId_;
globalDepositIdToWarChestDeposit[currentDepositId_] = warChestDeposit;
startingInfluence = _updateInfluenceStats(
user,
influence,
amount,
day,
isWarChestTriggered,
DepositAction.START
);
emit WarChestDeposited(user, currentDepositId_, lockPeriod, warChestDeposit);
}
/** @dev withdraws warChest deposit
* @param user user address
* @param depositId depositId
* @param day current protocol day
* @param action deposit action
* @param isWarChestTriggered was WarChest Triggered
* @return wyvern withdrawal amount
*/
function _withdrawWarChestDeposit(
address user,
uint256 depositId,
uint256 day,
DepositAction action,
WarChestLootTriggered isWarChestTriggered
) internal returns (uint256 wyvern) {
uint256 globalDepositId = addressDepositIdToGlobalDepositId[user][depositId];
if (globalDepositId == 0) revert NoDeposits();
WarChestDeposit memory warChestDeposit = globalDepositIdToWarChestDeposit[globalDepositId];
if (warChestDeposit.status == DepositStatus.ENDED) revert DepositAlreadyWithdrawn();
uint256 influence = warChestDeposit.influence;
_updateInfluenceStats(user, influence, warChestDeposit.wyvernAmount, day, isWarChestTriggered, action);
if (action == DepositAction.END) {
++totalWithdrawnDeposits;
globalDepositIdToWarChestDeposit[globalDepositId].status = DepositStatus.ENDED;
}
wyvern = _calcWithdrawalAmount(user, globalDepositId, warChestDeposit);
}
/** @dev update stats related to Hunter
* @param currentWRank current wRank
* @param guildStrength current global guild Strength
* @param totalLooting current total Wyvern Being Looted
*/
function _updateHunterStats(uint256 currentWRank, uint256 guildStrength, uint256 totalLooting) internal {
globalWRank = currentWRank;
globalGuildStrength = guildStrength;
totalWyvernBeingLooted = totalLooting;
}
/** @dev updates influence stats
* @param user user address
* @param influence influence
* @param amount WyvernX amount
* @param day current contract day
* @param isWarChestTriggered has WarChest been triggered
* @param action start or withdraw deposit
* @return startingInfluence startingInfluence protocol's first influence
*/
function _updateInfluenceStats(
address user,
uint256 influence,
uint256 amount,
uint256 day,
WarChestLootTriggered isWarChestTriggered,
DepositAction action
) private returns (uint256 startingInfluence) {
uint256 index = userInfluenceIndex[user];
uint256 previousShares = addressIdToCurrentInfluence[user][index].currentInfluence;
if (action == DepositAction.START) {
if (index == 0) startingInfluence = 1;
addressIdToCurrentInfluence[user][++index].currentInfluence = previousShares + influence;
totalInfluence += influence;
totalWyvernInWarChests += amount;
} else {
addressIdToCurrentInfluence[user][++index].currentInfluence = previousShares - influence;
totalExpiredInfluence += influence;
totalWyvernInWarChests -= amount;
}
addressIdToCurrentInfluence[user][index].day =
isWarChestTriggered == WarChestLootTriggered.NO ? day : day + 1;
userInfluenceIndex[user] = index;
}
/** @dev calculate final loot
* @param user user address
* @param wRank hunter's wRank
* @param guildStrength guild Strength
* @param hunter hunter stats
* @param action claim hunter
* @return loot final loot with all bonuses
*/
function _calculateClaimLoot(
address user,
uint256 wRank,
uint256 guildStrength,
Hunter memory hunter,
HunterAction action
) private returns (uint256 loot) {
if (action == HunterAction.CLAIM) wRankToHunter[wRank].status = HunterStatus.CLAIMED;
uint256 bonus;
if (action == HunterAction.CLAIM) {
bonus = calculateGuildStrengthBonus(
hunter.hunterStrengthBonus,
hunter.strength,
guildStrength,
globalGuildStrength
);
}
loot = uint256(hunter.lootableWyvern) + (bonus / SCALING_FACTOR_1e7);
if (action == HunterAction.CLAIM) ++totalHuntersClaimed;
if (action == HunterAction.CLAIM) wRankToHunter[wRank].lootedWyvern = loot;
emit HunterClaimed(user, wRank, loot);
}
/** @dev calculate amount of Wyvern to withdraw
* @param user user address
* @param globalDepositId global depositId
* @param warChestDeposit warChest Deposit
* @return toWithdraw amount to withdraw after penalties
*/
function _calcWithdrawalAmount(
address user,
uint256 globalDepositId,
WarChestDeposit memory warChestDeposit
) internal returns (uint256 toWithdraw) {
uint256 wyvernAmount = warChestDeposit.wyvernAmount;
uint256 penalty = calcWithdrawalPenalty(
warChestDeposit.depositTimestamp,
warChestDeposit.unlockTimestamp,
block.timestamp
);
uint256 penaltyAmount;
penaltyAmount = (wyvernAmount * penalty) / 100;
toWithdraw = wyvernAmount - penaltyAmount;
totalWithdrawalPenalties += penaltyAmount;
emit WarChestWithdrawn(user, globalDepositId, toWithdraw, penalty, penaltyAmount);
}
// === Global State Accessors ===
/** @notice Return current global wyvernRank
* @return globalWRank global wRank
*/
function getGlobalWRank() public view returns (uint256) {
return globalWRank;
}
/** @notice Return current global Guild Strength
* @return globalGuildStrength global Guild Strength
*/
function getGlobalGuildStrength() public view returns (uint256) {
return globalGuildStrength;
}
/** @notice Return total Hunters Claimed
* @return totalMintClaimed total Hunters Claimed
*/
function getTotalHuntersClaimed() public view returns (uint256) {
return totalHuntersClaimed;
}
/** @notice Return total looted Wyvern
* @return totalLootedWyvern total Looted Wyvern
*/
function getTotalLootedWyvern() public view returns (uint256) {
return totalLootedWyvern;
}
/** @notice Return total Wyvern Being Looted
* @return totalWyvernBeingLooted total Wyvern Being Looted
*/
function getTotalWyvernBeingLooted() public view returns (uint256) {
return totalWyvernBeingLooted;
}
/** @notice get total Influence
* @return totalInfluence total Influence
*/
function getTotalInfluence() public view returns (uint256) {
return totalInfluence;
}
/** @notice get total Expired Influence
* @return totalExpiredInfluence total Expired Influence
*/
function getTotalExpiredInfluence() public view returns (uint256) {
return totalExpiredInfluence;
}
/** @notice get guilds total Influence
* @return protocolTotalInfluence guilds total Influence
*/
function getProtocolTotalInfluence() public view returns (uint256) {
return totalInfluence - totalExpiredInfluence;
}
/** @notice get total Wyvern deposited in WarChests
* @return totalWyvernInWarChests total Wyvern In WarChests
*/
function getTotalWyvernDeposited() public view returns (uint256) {
return totalWyvernInWarChests;
}
/** @notice get guilds depositId
* @return currentDepositId guilds current depositId
*/
function getCurrentDepositId() public view returns (uint256) {
return currentDepositId;
}
/** @notice get total Withdrawal Penalties
* @return totalWithdrawalPenalties total Withdrawal Penalties
*/
function getTotalWithdrawalPenalties() public view returns (uint256) {
return totalWithdrawalPenalties;
}
/** @notice get total active deposits
* @return totalActiveDeposits Total Active Deposits
*/
function getTotalActiveDeposits() public view returns (uint256) {
return currentDepositId - getTotalWithdrawnDeposits();
}
/** @notice get guild's total Withdrawn Deposits
* @return totalWithdrawnDeposits total Withdrawn Deposits
*/
function getTotalWithdrawnDeposits() public view returns (uint256) {
return totalWithdrawnDeposits;
}
// === User-Specific Functions ===
/** @notice Returns the latest hunter Id of an address
* @return hunterId latest hunter id
*/
function getUserLatestHunterId(address user) public view returns (uint256) {
return addressHId[user];
}
/** @notice Returns hunter stats of an address
* @param user address
* @param id hunter id
* @return hunter user hunter stats
*/
function getHunter(
address user,
uint256 id
) public view returns (Hunter memory hunter) {
return wRankToHunter[addressHIdToWyvernRank[user][id].wRank];
}
/** @notice Return all userHunters of an address
* @return userHunter all userHunters of an address
*/
function getUserHunters(address user) public view returns (UserHunter[] memory userHunter) {
uint256 count = addressHId[user];
userHunter = new UserHunter[](count);
for (uint256 i = 1; i <= count; i++) {
userHunter[i - 1] = UserHunter({
hunterId: i,
wRank: addressHIdToWyvernRank[user][i].wRank,
guildStrength: addressHIdToWyvernRank[user][i].guildStrength,
hunter: getHunter(user, i)
});
}
}
/** @notice get deposit stats
* @return warChestDeposit deposit stats
*/
function getUserWarChestDeposit(address user, uint256 id) public view returns (WarChestDeposit memory) {
return globalDepositIdToWarChestDeposit[addressDepositIdToGlobalDepositId[user][id]];
}
/** @notice get all warChest deposits of an address
* @return userWarChest all warChest deposits of an address
*/
function getUserWarChests(address user) public view returns (UserWarChest[] memory) {
uint256 count = addressDepositId[user];
UserWarChest[] memory userWarChest = new UserWarChest[](count);
for (uint256 i = 1; i <= count; i++) {
userWarChest[i - 1] = UserWarChest({
depositId: i,
currentDepositId: addressDepositIdToGlobalDepositId[user][i],
warChestDeposit: getUserWarChestDeposit(user, i)
});
}
return userWarChest;
}
// === User Influence Functions ===
/** @notice get user latest Influence Index
* @return userInfluenceIndex latest user Influence Index
*/
function getUserCurrentInfluenceIndex(address user) public view returns (uint256) {
return userInfluenceIndex[user];
}
/** @notice get user current influence
* @return currentInfluence current Influence
*/
function getUserCurrentInfluence(address user) public view returns (uint256) {
return addressIdToCurrentInfluence[user][getUserCurrentInfluenceIndex(user)].currentInfluence;
}
/** @notice get user influence from influenceIndex snapshot
* @return influence active influence at influenceIndex
*/
function getUserInfluenceAtIndex(
address user,
uint256 influenceIndex
) internal view returns (uint256) {
return addressIdToCurrentInfluence[user][influenceIndex].currentInfluence;
}
/** @notice get user influenceDay from influenceIndex
* @return influenceDay protocol's day stored at influenceIndex
*/
function getUserInfluenceDayFromInfluenceIndex(
address user,
uint256 influenceIndex
) internal view returns (uint256) {
return addressIdToCurrentInfluence[user][influenceIndex].day;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/ITITANX.sol";
import "../contracts/WyvernVault.sol";
import "../contracts/WyvernX.sol";
import "./constant.sol";
/**
* @title WyvernVault's staking module
* @notice Operates as part of WyvernVault, not standalone
*/
contract TitanStaker is Ownable {
using SafeERC20 for IERC20;
using SafeERC20 for ITitanX;
uint256 public activeTitanXStakes;
error StakeNotCompleted();
constructor() Ownable(msg.sender) {}
// NATIVE TOKEN HANDLING
/**
* @notice Processes incoming native token deposits
* Reverts if the sender is not TitanX contract.
*/
receive() external payable {
require(msg.sender == TITANX_ADDRESS, "Sender is not TitanX");
}
/**
* @notice Handles unexpected contract interactions
* @dev Blocks unintended native token transfers
*/
fallback() external payable {
revert("Fallback triggered");
}
/**
* @notice Locks available TitanX in max-length stake
*/
function stake() external onlyOwner {
ITitanX titanX = ITitanX(TITANX_ADDRESS);
uint256 amountToStake = titanX.balanceOf(address(this));
titanX.startStake(amountToStake, TITANX_MAX_STAKE_LENGTH);
activeTitanXStakes += 1;
}
/**
* @notice Collects ETH rewards from TitanX staking
* @return pendingReward ETH amount to be claimed
*/
function claim() external onlyOwner returns (uint256 pendingReward) {
ITitanX titanX = ITitanX(TITANX_ADDRESS);
pendingReward = titanX.getUserETHClaimableTotal(address(this));
if (pendingReward > 0) {
titanX.claimUserAvailableETHPayouts();
Address.sendValue(payable(owner()), pendingReward);
}
}
/**
* @dev Unlocks completed stake and returns tokens to vault
* @param stakeId Target stake identifier
* @notice Requires completed lock period
*/
function withdrawCompletedStake(uint256 stakeId) external {
ITitanX titanX = ITitanX(TITANX_ADDRESS);
require(stakeId > 0 && stakeId <= activeTitanXStakes, "Invalid stake ID");
UserStakeInfo memory stakeInfo = titanX.getUserStakeInfo(address(this), stakeId);
if (block.timestamp >= stakeInfo.maturityTs) {
uint256 balanceBefore = titanX.balanceOf(address(this));
titanX.endStake(stakeId);
uint256 withdrawnAmount = titanX.balanceOf(address(this)) - balanceBefore;
IERC20(TITANX_ADDRESS).safeTransfer(owner(), withdrawnAmount);
WyvernVault(payable(owner())).stakeEnded(withdrawnAmount);
} else {
revert StakeNotCompleted();
}
}
/**
* @dev Checks available ETH rewards
* @return pendingReward ETH rewards ready to be claimed
*/
function totalPendingEthRewards() external view returns (uint256 pendingReward) {
ITitanX titanX = ITitanX(TITANX_ADDRESS);
pendingReward = titanX.getUserETHClaimableTotal(address(this));
}
/**
* @dev Finds completed stakes
* @return isCompleted Status of completed stake
* @return completedStakeId Id of completed stake
*/
function checkForCompletedStakes() external view returns (bool isCompleted, uint256 completedStakeId) {
ITitanX titanX = ITitanX(TITANX_ADDRESS);
UserStake[] memory activeStakes = titanX.getUserStakes(address(this));
for (uint256 i; i < activeStakes.length; i++) {
if (block.timestamp > activeStakes[i].stakeInfo.maturityTs) {
return (true, activeStakes[i].sId);
}
}
return (false, 0);
}
/**
* @dev Returns TitanX to vault
* @notice Recovery for locked tokens
*/
function recoverTitanX() external {
IERC20 titanX = IERC20(TITANX_ADDRESS);
titanX.safeTransfer(owner(), titanX.balanceOf(address(this)));
WyvernVault(payable(owner())).updateVault();
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
/* Universal */
uint256 constant SECONDS_IN_DAY = 86400;
uint256 constant SCALING_FACTOR_1e2 = 1e2;
uint256 constant SCALING_FACTOR_1e3 = 1e3;
uint256 constant SCALING_FACTOR_1e4 = 1e4;
uint256 constant SCALING_FACTOR_1e6 = 1e6;
uint256 constant SCALING_FACTOR_1e7 = 1e7;
uint256 constant SCALING_FACTOR_1e11 = 1e11;
uint256 constant SCALING_FACTOR_1e18 = 1e18;
/* Uniswap */
uint24 constant FEE_TIER = 10000;
int24 constant MIN_TICK = -887200;
int24 constant MAX_TICK = 887200;
/* Addresses */
address constant TITANX_ADDRESS = 0xF19308F923582A6f7c465e5CE7a9Dc1BEC6665B1;
address constant WETH9_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant UNI_SWAP_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
address constant UNI_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address constant UNI_NONFUNGIBLEPOSITIONMANAGER = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88;
/* WyvernX Const */
uint256 constant PERCENT_TO_CYCLE_WARCHESTS = 15_00;
uint256 constant PERCENT_TO_WYVERN_VAULT = 73_00;
uint256 constant PERCENT_TO_GENESIS = 7_00;
uint256 constant PERCENT_TO_LIQUIDITY_BONDING = 5_00; // 5%
uint256 constant REWARD_FEE_PERCENT = 30000;
uint256 constant REWARD_FEE_PERCENTAGE_BASE_WYVERNX = 1_000_000;
address constant LIQUIDITY_BONDING_ADDR = 0x41Df935aD61A2Df4e85bbD2a73CF7610d836f640;
uint256 constant AUTO_ENABLE_MINT_CLAIMING_DAY = 290;
uint256 constant DAILY_LOOTABLE_WYVERN_REDUCTION = 99_65;
uint256 constant STARTING_MAX_DAILY_LOOTABLE_WYVERN = 150 ether;
uint256 constant CAPPED_MIN_DAILY_LOOTABLE_WYVERN = 10 ether;
uint256 constant MINIMUM_SHAREPOOL_FOR_MINTING_PHASE = 41 * SCALING_FACTOR_1e4; // 41%
uint256 constant MINIMUM_SHAREPOOL_FOR_STAKING_PHASE = 51 * SCALING_FACTOR_1e4; // 51%
uint256 constant STARTING_INFLUENCE_RATE = 800 ether;
uint256 constant DAILY_SHARE_RATE_INCREASE_STEP = 100_07;
uint256 constant CAPPED_MAX_RATE = 2_800 ether;
//WarChest Const
uint256 constant DAY8 = 8;
uint256 constant DAY28 = 28;
uint256 constant DAY90 = 90;
uint256 constant DAY369 = 369;
uint256 constant DAY888 = 888;
uint256 constant CYCLE_8_PERCENT = 13_00;
uint256 constant CYCLE_28_PERCENT = 20_00;
uint256 constant CYCLE_90_PERCENT = 25_00;
uint256 constant CYCLE_369_PERCENT = 30_00;
uint256 constant CYCLE_888_PERCENT = 12_00;
uint256 constant PERCENT_BPS = 100_00;
//Hunters Claiming Const
uint256 constant MAX_HUNTER_STRENGTH_CAP = 100;
uint256 constant MAX_HUNT_LENGTH = 280;
uint256 constant MINTING_FREEZE_PERIOD = 281;
uint256 constant MAX_BATCH_HIRE_COUNT = 100;
uint256 constant MAX_HUNTERS_PER_WALLET = 1000;
uint256 constant MINT_DAILY_REDUCTION = 11;
uint256 constant STARTING_MAX_HUNTER_COST = 2_800_000_000 ether;
uint256 constant CAPPED_MAX_HUNTER_COST = 3_800_000_000 ether;
uint256 constant DAILY_HUNTER_COST_INCREASE = 100_08;
uint256 constant STARTING_HUNTER_STRENGTH_BONUS = 2 * SCALING_FACTOR_1e6;
uint256 constant MIN_HUNTER_STRENGTH_BONUS = 2 * SCALING_FACTOR_1e2;
uint256 constant DAILY_MINTPOWER_INCREASE_BONUS_REDUCTION = 99_65;
uint256 constant STARTING_EH_BONUS = 10 * SCALING_FACTOR_1e6;
uint256 constant EH_BONUS_FIXED_REDUCTION_PER_DAY = 28_571;
uint256 constant EH_BONUS_END = 0;
uint256 constant MAX_BONUS_DAY = 281;
uint256 constant MAX_DEPOSITS_PER_WALLET = 1000;
uint256 constant MIN_LOCK_PERIOD = 28;
uint256 constant MAX_LOCK_PERIOD = 3500;
uint256 constant WITHDRAW_GOODWILL_PERIOD = 20;
uint256 constant LPB_MAX_DAYS = 2888;
uint256 constant LPB_PER_PERCENT = 825;
uint256 constant BPB_MAX_WYVERN = 200_000 * SCALING_FACTOR_1e18; // 200,000
uint256 constant BPB_PER_PERCENT = 2_500_000 * SCALING_FACTOR_1e18;
/* WyvernVault Const */
uint256 constant VAULT_DISTRIBUTION = 7300; // 73%
uint256 constant WYVERN_STAKING_DISTRIBUTION = 2200; // 22%
uint256 constant REWARD_FEE = 300; // 3%
uint256 constant TINC_BURN_DISTRIBUTION = 500; // 5%
uint256 constant PERCENTAGE_BASE_VAULT = 10_000;
uint256 constant STAKE_COOLDOWN_PERIOD = 10 days;
/* TitanX Protocol Const */
/* Staking */
uint256 constant TITANX_MAX_STAKE_PER_WALLET = 1000;
uint256 constant TITANX_MIN_STAKE_LENGTH = 28;
uint256 constant TITANX_MAX_STAKE_LENGTH = 3500;
/* Stake bonuses */
uint256 constant TITANX_LPB_MAX_DAYS = 2888;
uint256 constant TITANX_LPB_PER_PERCENT = 825;
uint256 constant TITANX_BPB_MAX_TITAN = 100 * 1e9 * SCALING_FACTOR_1e18; //100 billion
uint256 constant TITANX_BPB_PER_PERCENT = 1_250_000_000_000 *
SCALING_FACTOR_1e18;// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
enum WyvernManaPoolMinted {
NO,
YES
}
enum WyvernMintingPhaseFinished {
NO,
YES
}
enum ClaimingEnabled {
NO,
YES
}
enum HunterAction {
CLAIM
}
enum HunterStatus {
ACTIVE,
CLAIMED
}
enum DepositAction {
START,
END
}
enum DepositStatus {
ACTIVE,
ENDED
}
enum WarChestLootTriggered {
NO,
YES
}
enum CycleLootClaim {
INFLUENCE
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "./constant.sol";
import "./enum.sol";
/** @notice get batch mint cost
* @param mintPower mint power (1 - 100)
* @param count number of mints
* @return mintCost total mint cost
*/
function getBatchHunterCost(
uint256 mintPower,
uint256 count,
uint256 mintCost
) pure returns (uint256) {
return (mintCost * mintPower * count) / MAX_HUNTER_STRENGTH_CAP;
}
/** @notice the formula to calculate mint reward at create new mint
* @param mintPower mint power 1 - 100
* @param numOfDays mint length 1 - 280
* @param mintableWyvern current contract day mintable Wyvern
* @param EAABonus current contract day EAA Bonus
* @return reward base Wyvern amount
*/
function calculateHunterReward(
uint256 mintPower,
uint256 numOfDays,
uint256 mintableWyvern,
uint256 EAABonus
) pure returns (uint256 reward) {
uint256 baseReward = (mintableWyvern * mintPower * numOfDays);
if (numOfDays != 1)
baseReward -= (baseReward * MINT_DAILY_REDUCTION * (numOfDays - 1)) / PERCENT_BPS;
reward = baseReward;
if (EAABonus != 0) {
//EAA Bonus has 1e6 scaling, so here divide by 1e6
reward += ((baseReward * EAABonus) / 100 / SCALING_FACTOR_1e6);
}
reward /= MAX_HUNTER_STRENGTH_CAP;
}
/** @notice the formula to calculate bonus reward
* heavily influenced by the difference between current global mint power and user mint's global mint power
* @param hunterStrengthBonus hunter Strength Bonus from hunter
* @param strength hunter strength 1 - 100 snapshot from hunter
* @param guildStrength guild Strength snapshot from hunter
* @param globalGuildStrength current global Guild Strength
* @return bonus bonus amount in Wyvern
*/
function calculateGuildStrengthBonus(
uint256 hunterStrengthBonus,
uint256 strength,
uint256 guildStrength,
uint256 globalGuildStrength
) pure returns (uint256 bonus) {
if (globalGuildStrength <= guildStrength) return 0;
bonus = (((hunterStrengthBonus * strength * (globalGuildStrength - guildStrength)) * SCALING_FACTOR_1e18) /
MAX_HUNTER_STRENGTH_CAP);
}
error WithdrawalNotYetPossible();
/** @notice get max stake length
* @return maxStakeLength max stake length
*/
function getMaxStakeLength() pure returns (uint256) {
return MAX_LOCK_PERIOD;
}
/** @notice calculate shares and shares bonus
* @param amount WyvernX amount
* @param noOfDays stake length
* @param shareRate current contract share rate
* @return shares calculated shares in 18 decimals
*/
function calculateInfluence(
uint256 amount,
uint256 noOfDays,
uint256 shareRate
) pure returns (uint256) {
uint256 shares = amount;
shares += (shares * calculateShareBonus(amount, noOfDays)) / SCALING_FACTOR_1e11;
shares /= (shareRate / SCALING_FACTOR_1e18);
return shares;
}
/** @notice calculate share bonus
* @param amount Wyvern amount
* @param noOfDays stake length
* @return shareBonus calculated shares bonus in 11 decimals
*/
function calculateShareBonus(uint256 amount, uint256 noOfDays) pure returns (uint256 shareBonus) {
uint256 cappedExtraDays = noOfDays <= LPB_MAX_DAYS ? noOfDays : LPB_MAX_DAYS;
uint256 cappedStakedWyvern = amount <= BPB_MAX_WYVERN ? amount : BPB_MAX_WYVERN;
shareBonus =
((cappedExtraDays * SCALING_FACTOR_1e11) / LPB_PER_PERCENT) +
((cappedStakedWyvern * SCALING_FACTOR_1e11) / BPB_PER_PERCENT);
return shareBonus;
}
/** @notice calculate Withdrawal Penalty
* @param depositTimestamp deposit Timestamp
* @param unlockTimestamp unlock Timestamp
* @param blockTimestamp current block timestamp
* @return penalty penalty in percentage
*/
function calcWithdrawalPenalty(
uint256 depositTimestamp,
uint256 unlockTimestamp,
uint256 blockTimestamp
) view returns (uint256) {
if (blockTimestamp > unlockTimestamp) {
uint256 timeLateSinceUnlock = blockTimestamp - unlockTimestamp;
uint256 withdrawGoodwillInSec = WITHDRAW_GOODWILL_PERIOD * SECONDS_IN_DAY;
if (timeLateSinceUnlock <= withdrawGoodwillInSec) return 0;
return max((min((timeLateSinceUnlock - withdrawGoodwillInSec), 1) / SECONDS_IN_DAY) + 1, 99);
}
// Withdrawal not possible if 50% of the unlock time has passed
if (block.timestamp < depositTimestamp + (unlockTimestamp - depositTimestamp) / 2) revert WithdrawalNotYetPossible();
//50% penalty for unlocking early
return 50;
}
//a - input to check against b
//b - minimum number
function min(uint256 a, uint256 b) pure returns (uint256) {
if (a > b) return a;
return b;
}
//a - input to check against b
//b - maximum number
function max(uint256 a, uint256 b) pure returns (uint256) {
if (a > b) return b;
return a;
}
// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
// but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
z := sub(z, lt(div(x, z), z))
}
}{
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"titanBuyAddress_","type":"address"},{"internalType":"address","name":"wyvernBuyAndBurnAdddress_","type":"address"},{"internalType":"bytes32","name":"deploymentKey_","type":"bytes32"}],"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":"CooldownPeriodActive","type":"error"},{"inputs":[],"name":"Create2EmptyBytecode","type":"error"},{"inputs":[],"name":"Create2FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"Create2InsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientBalanceForCallerReward","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidWyvernAddress","type":"error"},{"inputs":[],"name":"NewTitanStakerNotNeeded","type":"error"},{"inputs":[],"name":"NoMoreStakesAllowed","type":"error"},{"inputs":[],"name":"NoPendingEthRewards","type":"error"},{"inputs":[],"name":"NoTokensToStake","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":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"totalCollected","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"titanBuy","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wyvernBuyAndBurn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"genesis","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardFee","type":"uint256"}],"name":"Collected","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":"titanStakerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TitanStakeStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerContractId","type":"uint256"},{"indexed":true,"internalType":"address","name":"stakerContractAddress","type":"address"}],"name":"TitanStakerCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"titanStakerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TitanStakesEnded","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"activeTitanStakerContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowTitanStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"callerRewardFeeForCallingClaim","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[{"internalType":"uint256","name":"collectedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"deploymentKey","type":"bytes32"}],"name":"createNewTitanStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllCompletedStakes","outputs":[{"internalType":"bool","name":"hasCompletedStakes","type":"bool"},{"internalType":"address","name":"titanStakerAddress","type":"address"},{"internalType":"uint256","name":"stakeId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCombinedWyvernActiveShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalTitanBalanceInVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWyvernActiveSharesPercentage","outputs":[{"internalType":"uint256","name":"percentage","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTitanStakeTimestamp","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":"address","name":"titanBuyAddress_","type":"address"}],"name":"setTitanBuyContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wyvernBuyAndBurnAddress_","type":"address"}],"name":"setWyvernBuyAndBurnContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountWithdrawn","type":"uint256"}],"name":"stakeEnded","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"titanBuyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"titanStakerContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"titanStakerContractsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"titanStakingStartTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalActiveStakesInTitanStakers","outputs":[{"internalType":"uint256","name":"totalActiveStakes","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEthRewardsCollected","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPendingEthRewards","outputs":[{"internalType":"uint256","name":"pendingRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTitanStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTitanUnstaked","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":"updateVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"withdrawFromGenesis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wyvernBuyAndBurnAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60806040523480156200001157600080fd5b50604051620047a9380380620047a9833981016040819052620000349162000526565b33806200005c57604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b62000067816200014a565b5060016002556001600160a01b038316620000955760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b038216620000bd5760405163e6c4247b60e01b815260040160405180910390fd5b600380546001600160a01b038086166001600160a01b031992831617909255600480549285169290911691909117905573f19308f923582a6f7c465e5ce7a9dc1bec6665b1600052600f6020527f0bb920746b4d39e69695097c53a5db3980ccc8cd33fcceab7240adceb3fddb62805460ff19166001179055620001418162000168565b50505062000602565b600180546001600160a01b03191690556200016581620003f6565b50565b6000604051806020016200017c90620004fb565b601f1982820381018352601f9091011660408181523060208301520160408051601f1981840301815290829052620001b8929160200162000599565b60408051601f1981840301815291905260065490915060003082620001df600143620005d0565b60405160609390931b6001600160601b0319166020840152603483019190915240605482015260748101859052609401604051602081830303815290604052805190602001209050600062000243828580519060200120306200044660201b60201c565b9050803b15620002965760405162461bcd60e51b815260206004820152601660248201527f4164647265737320616c72656164792065786973747300000000000000000000604482015260640162000053565b6000620002a581848762000470565b90506001600160a01b038116620002f35760405162461bcd60e51b815260206004820152601160248201527011195c1b1bde5b595b9d0819985a5b1959607a1b604482015260640162000053565b816001600160a01b0316816001600160a01b031614620003495760405162461bcd60e51b815260206004820152601060248201526f082c8c8e4cae6e640dad2e6dac2e8c6d60831b604482015260640162000053565b600780546001600160a01b0383166001600160a01b031991821681179092556000868152600d6020908152604080832080549094168517909355838252600f8152828220805460ff19908116600190811790925560109092528383208054909216179055905186917f65c6cd255dd577a3cfa318bf276e250034525393db6a9055f18c0baee6a1ba7c91a3600160066000828254620003e99190620005ec565b9091555050505050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b6000834710156200049e5760405163392efb2b60e21b81524760048201526024810185905260440162000053565b8151600003620004c157604051631328927760e21b815260040160405180910390fd5b8282516020840186f590506001600160a01b038116620004f457604051633a0ba96160e11b815260040160405180910390fd5b9392505050565b611189806200362083390190565b80516001600160a01b03811681146200052157600080fd5b919050565b6000806000606084860312156200053c57600080fd5b620005478462000509565b9250620005576020850162000509565b9150604084015190509250925092565b6000815160005b818110156200058a57602081850181015186830152016200056e565b50600093019283525090919050565b6000620005b2620005ab838662000567565b8462000567565b949350505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115620005e657620005e6620005ba565b92915050565b80820180821115620005e657620005e6620005ba565b61300e80620006126000396000f3fe608060405260043610620002175760003560e01c806379ba5097116200011b578063b92811c211620000a3578063ed17b5b4116200006d578063ed17b5b4146200063c578063f2fde38b1462000661578063fb1de4181462000686578063fbfa77cf146200069e576200027a565b8063b92811c214620005c0578063cbae3c1714620005d8578063d1a8580414620005fa578063e30c3978146200061c576200027a565b80638da5cb5b11620000e55780638da5cb5b14620005365780639a28b6c51462000556578063a684483d1462000590578063b4b140b514620005a8576200027a565b806379ba509714620004bc5780638096574d14620004d45780638248a3b814620004f95780638b3f7899146200051e576200027a565b806349f16525116200019f57806365291e3c116200016957806365291e3c1462000436578063673e4cbe1462000474578063715018a6146200048c5780637196e84114620004a4576200027a565b806349f1652514620003c95780634e71d92d14620003ee57806357f73ebc14620004065780635ba82b4b146200041e576200027a565b80633a4b66f111620001e15780633a4b66f114620003695780633c63f22614620003815780633d47c34b14620003995780634439131f14620003b1576200027a565b80630da996a414620002c65780631007776514620002f157806317a25931146200032c5780633929ddc21462000344576200027a565b366200027a57336000908152600f602052604090205460ff16620002785760405162461bcd60e51b815260206004820152601360248201527214d95b99195c881d5b985d5d1a1bdc9a5e9959606a1b60448201526064015b60405180910390fd5b005b3480156200028757600080fd5b5060405162461bcd60e51b815260206004820152601260248201527111985b1b189858dac81d1c9a59d9d95c995960721b60448201526064016200026f565b348015620002d357600080fd5b50620002de620006b6565b6040519081526020015b60405180910390f35b348015620002fe57600080fd5b5060075462000313906001600160a01b031681565b6040516001600160a01b039091168152602001620002e8565b3480156200033957600080fd5b50620002de600c5481565b3480156200035157600080fd5b50620002786200036336600462001c70565b62000782565b3480156200037657600080fd5b506200027862000828565b3480156200038e57600080fd5b50620002de60085481565b348015620003a657600080fd5b50620002786200095c565b348015620003be57600080fd5b50620002de62000a0f565b348015620003d657600080fd5b5062000278620003e836600462001c8a565b62000ac5565b348015620003fb57600080fd5b50620002de62000b19565b3480156200041357600080fd5b50620002de60065481565b3480156200042b57600080fd5b50620002de62000e60565b3480156200044357600080fd5b506200044e62000f13565b6040805193151584526001600160a01b03909216602084015290820152606001620002e8565b3480156200048157600080fd5b50620002de62000fe6565b3480156200049957600080fd5b5062000278620010b0565b348015620004b157600080fd5b5062000278620010c8565b348015620004c957600080fd5b506200027862001172565b348015620004e157600080fd5b5062000278620004f336600462001c8a565b620011ba565b3480156200050657600080fd5b50620002786200051836600462001c70565b62001281565b3480156200052b57600080fd5b50620002de6200132d565b3480156200054357600080fd5b506000546001600160a01b031662000313565b3480156200056357600080fd5b50620003136200057536600462001c70565b600d602052600090815260409020546001600160a01b031681565b3480156200059d57600080fd5b50620002de60095481565b348015620005b557600080fd5b50620002de600a5481565b348015620005cd57600080fd5b50620002de600b5481565b348015620005e557600080fd5b5060035462000313906001600160a01b031681565b3480156200060757600080fd5b5060045462000313906001600160a01b031681565b3480156200062957600080fd5b506001546001600160a01b031662000313565b3480156200064957600080fd5b50620002786200065b36600462001c8a565b62001357565b3480156200066e57600080fd5b50620002786200068036600462001c8a565b620013ab565b3480156200069357600080fd5b50620002de6200141f565b348015620006ab57600080fd5b50620002de60055481565b600080620006c362000fe6565b9050600073f19308f923582a6f7c465e5ce7a9dc1bec6665b190506000816001600160a01b031663af4fb7636040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200071f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000745919062001cb5565b9050806000036200075a576000935050505090565b60006200076b620f42408562001ce5565b905062000779828262001d15565b94505050505090565b6007546040805163cd652a1560e01b815290516001600160a01b03909216916103e891839163cd652a15916004808201926020929091908290030181865afa158015620007d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007f9919062001cb5565b101562000819576040516330f90d9f60e11b815260040160405180910390fd5b6200082482620014ce565b5050565b6007546040805163cd652a1560e01b815290516001600160a01b03909216916103e891839163cd652a15916004808201926020929091908290030181865afa15801562000879573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200089f919062001cb5565b10620008be5760405163807877a160e01b815260040160405180910390fd5b620008c8620010c8565b6005546000819003620008ee5760405163598172d360e11b815260040160405180910390fd5b62000907670de0b6b3a764000064174876e80062001ce5565b81106200092e576200091862001755565b620009274262093a8062001d2c565b6009555050565b600954421015620009525760405163998d019b60e01b815260040160405180910390fd5b6200091862001755565b6004546001600160a01b0316338114620009c35760405162461bcd60e51b815260206004820152602160248201527f756e617574686f72697a656420746f20616c6c6f77546974616e5374616b696e6044820152606760f81b60648201526084016200026f565b426000620009d5620151808362001d42565b620009e4906201518062001d59565b9050620009f2818362001d2c565b600881905562000a0790620d2f009062001d2c565b600955505050565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b16000818152600e6020527f9be7d1ff6ec84e99c630ebb14733fbe0ea49d80048034391f860835147bfa560546040516370a0823160e01b8152306004820152919290916370a0823190602401602060405180830381865afa15801562000a8e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ab4919062001cb5565b62000ac0919062001d59565b905090565b62000acf62001844565b6001600160a01b03811662000af75760405163e6c4247b60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600062000b2562001873565b33321462000b46576040516348f5c3ed60e01b815260040160405180910390fd5b600047905073f19308f923582a6f7c465e5ce7a9dc1bec6665b16001600160a01b0316632277d1bd6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562000b9b57600080fd5b505af115801562000bb0573d6000803e3d6000fd5b505050506000814762000bc4919062001d59565b905060005b60065481101562000c78576000818152600d60209081526040808320548151634e71d92d60e01b815291516001600160a01b03909116938493634e71d92d9360048082019492939183900301908290875af115801562000c2d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c53919062001cb5565b62000c5f908662001d2c565b945050808062000c6f9062001d6f565b91505062000bc9565b508260000362000c9b576040516345f8828960e11b815260040160405180910390fd5b600061271062000cae856101f462001ce5565b62000cba919062001d15565b9050600061271062000ccf61012c8762001ce5565b62000cdb919062001d15565b9050600061271062000cf0876102bc62001ce5565b62000cfc919062001d15565b90506000818362000d0e868a62001d59565b62000d1a919062001d59565b62000d26919062001d59565b6000808052600e6020527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c8054929350869290919062000d6890849062001d2c565b909155505060045462000d85906001600160a01b0316836200189c565b60035462000d9d906001600160a01b0316826200189c565b33600062000dac878662001d2c565b90508047101562000dd05760405163b7b0514d60e01b815260040160405180910390fd5b62000ddc82826200189c565b88600c600082825462000df0919062001d2c565b909155505060408051848152602081018690529081018790526060810182905289906001600160a01b038416907f9e48e42df0747083f09111bd5a1d577e07fb9ef3a10a11a77eec61699396c4f09060800160405180910390a3505050505050505062000e5d6001600255565b90565b6000805b60065481101562000f0f576000818152600d602090815260409182902054825163cd652a1560e01b815292516001600160a01b0390911692839263cd652a15926004808401938290030181865afa15801562000ec4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000eea919062001cb5565b62000ef6908462001d2c565b925050808062000f069062001d6f565b91505062000e64565b5090565b6000806000805b60065481101562000fd8576000818152600d602052604080822054815163088db14d60e31b815282516001600160a01b0390921693849390928392859263446d8a68926004808401938290030181865afa15801562000f7d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000fa3919062001da1565b91509150811562000fbe576001989397509550919350505050565b50505050808062000fcf9062001d6f565b91505062000f1a565b506000938493508392509050565b60008060005b600654811015620010aa576000818152600d602052604090819020549051639a5a6cd960e01b81526001600160a01b03909116600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b190639a5a6cd990602401602060405180830381865afa15801562001061573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001087919062001cb5565b62001093908362001d2c565b915080620010a18162001d6f565b91505062000fec565b50919050565b620010ba62001844565b620010c6600062001938565b565b6040516370a0823160e01b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b19060009082906370a0823190602401602060405180830381865afa1580156200111d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001143919062001cb5565b6001600160a01b0383166000908152600e60205260409020549091506200116b908262001d59565b6005555050565b60015433906001600160a01b03168114620011ac5760405163118cdaa760e01b81526001600160a01b03821660048201526024016200026f565b620011b78162001938565b50565b620011c462001844565b6001600160a01b0381166000908152600e60205260408120805491905580620012255760405162461bcd60e51b81526020600482015260126024820152711b9bdd1a1a5b99c81d1bc818dbdb1b1958dd60721b60448201526064016200026f565b6001600160a01b0382166200125257620008246200124b6000546001600160a01b031690565b826200189c565b816200127c6200126a6000546001600160a01b031690565b6001600160a01b038316908462001953565b505050565b3360009081526010602052604090205460ff16620012d25760405162461bcd60e51b815260206004820152600d60248201526c1b9bdd081c195c9b5a5d1d1959609a1b60448201526064016200026f565b620012dc620010c8565b80600b6000828254620012f0919062001d2c565b909155505060405181815233907f2e9913a2a8c79eba012e824e0d321123748df7744db53fd740f0498c9c68ed679060200160405180910390a250565b600061271061012c6200133f6200141f565b6200134b919062001ce5565b62000ac0919062001d15565b6200136162001844565b6001600160a01b038116620013895760405163e6c4247b60e01b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b620013b562001844565b600180546001600160a01b0383166001600160a01b03199091168117909155620013e76000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000805b60065481101562000f0f576000818152600d6020908152604091829020548251631f63bc8360e31b815292516001600160a01b0390911692839263fb1de418926004808401938290030181865afa15801562001483573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620014a9919062001cb5565b620014b5908462001d2c565b9250508080620014c59062001d6f565b91505062001423565b600060405180602001620014e29062001c62565b601f1982820381018352601f9091011660408181523060208301520160408051601f19818403018152908290526200151e929160200162001e02565b60408051601f19818403018152919052600654909150600030826200154560014362001d59565b60405160609390931b6bffffffffffffffffffffffff191660208401526034830191909152406054820152607481018590526094016040516020818303038152906040528051906020012090506000620015a882858051906020012030620019a7565b9050803b15620015f45760405162461bcd60e51b81526020600482015260166024820152754164647265737320616c72656164792065786973747360501b60448201526064016200026f565b60006200160460008487620019d1565b90506001600160a01b038116620016525760405162461bcd60e51b815260206004820152601160248201527011195c1b1bde5b595b9d0819985a5b1959607a1b60448201526064016200026f565b816001600160a01b0316816001600160a01b031614620016a85760405162461bcd60e51b815260206004820152601060248201526f082c8c8e4cae6e640dad2e6dac2e8c6d60831b60448201526064016200026f565b600780546001600160a01b0383166001600160a01b031991821681179092556000868152600d6020908152604080832080549094168517909355838252600f8152828220805460ff19908116600190811790925560109092528383208054909216179055905186917f65c6cd255dd577a3cfa318bf276e250034525393db6a9055f18c0baee6a1ba7c91a360016006600082825462001748919062001d2c565b9091555050505050505050565b6007546005805460009091556001600160a01b039091169073f19308f923582a6f7c465e5ce7a9dc1bec6665b19082906200179283838362001953565b816001600160a01b0316633a4b66f16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620017ce57600080fd5b505af1158015620017e3573d6000803e3d6000fd5b5050505080600a6000828254620017fb919062001d2c565b90915550506040518181526001600160a01b038516907f8f8950cac73bae557ad3068a4fff5108d289ca6c6980f3eb364ccb555fec466c9060200160405180910390a250505050565b6000546001600160a01b03163314620010c65760405163118cdaa760e01b81523360048201526024016200026f565b60028054036200189657604051633ee5aeb560e01b815260040160405180910390fd5b60028055565b80471015620018c15760405163cd78605960e01b81523060048201526024016200026f565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811462001910576040519150601f19603f3d011682016040523d82523d6000602084013e62001915565b606091505b50509050806200127c57604051630a12f52160e11b815260040160405180910390fd5b600180546001600160a01b0319169055620011b78162001a5c565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526200127c90849062001aac565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b600083471015620019ff5760405163392efb2b60e21b8152476004820152602481018590526044016200026f565b815160000362001a2257604051631328927760e21b815260040160405180910390fd5b8282516020840186f590506001600160a01b03811662001a5557604051633a0ba96160e11b815260040160405180910390fd5b9392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600062001ac36001600160a01b0384168362001b16565b9050805160001415801562001aeb57508080602001905181019062001ae9919062001e23565b155b156200127c57604051635274afe760e01b81526001600160a01b03841660048201526024016200026f565b606062001b268383600062001b2f565b90505b92915050565b60608147101562001b565760405163cd78605960e01b81523060048201526024016200026f565b600080856001600160a01b0316848660405162001b74919062001e41565b60006040518083038185875af1925050503d806000811462001bb3576040519150601f19603f3d011682016040523d82523d6000602084013e62001bb8565b606091505b509150915062001bca86838362001bd4565b9695505050505050565b60608262001bed5762001be78262001c38565b62001a55565b815115801562001c0557506001600160a01b0384163b155b1562001c3057604051639996b31560e01b81526001600160a01b03851660048201526024016200026f565b508062001a55565b80511562001c495780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6111898062001e5083390190565b60006020828403121562001c8357600080fd5b5035919050565b60006020828403121562001c9d57600080fd5b81356001600160a01b038116811462001a5557600080fd5b60006020828403121562001cc857600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141762001b295762001b2962001ccf565b634e487b7160e01b600052601260045260246000fd5b60008262001d275762001d2762001cff565b500490565b8082018082111562001b295762001b2962001ccf565b60008262001d545762001d5462001cff565b500690565b8181038181111562001b295762001b2962001ccf565b60006001820162001d845762001d8462001ccf565b5060010190565b8051801515811462001d9c57600080fd5b919050565b6000806040838503121562001db557600080fd5b62001dc08362001d8b565b9150602083015190509250929050565b6000815160005b8181101562001df3576020818501810151868301520162001dd7565b50600093019283525090919050565b600062001e1b62001e14838662001dd0565b8462001dd0565b949350505050565b60006020828403121562001e3657600080fd5b62001b268262001d8b565b600062001b26828462001dd056fe608060405234801561001057600080fd5b50338061003757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61004081610046565b50610096565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6110e4806100a56000396000f3fe6080604052600436106100955760003560e01c80638da5cb5b116100595780638da5cb5b146101cb578063a0a4a299146101f3578063cd652a1514610213578063f2fde38b14610229578063fb1de41814610249576100fb565b80633a4b66f114610138578063446d8a681461014d5780634e71d92d1461017e5780635ad3f99c146101a1578063715018a6146101b6576100fb565b366100fb573373f19308f923582a6f7c465e5ce7a9dc1bec6665b1146100f95760405162461bcd60e51b81526020600482015260146024820152730a6cadcc8cae440d2e640dcdee840a8d2e8c2dcb60631b60448201526064015b60405180910390fd5b005b60405162461bcd60e51b815260206004820152601260248201527111985b1b189858dac81d1c9a59d9d95c995960721b60448201526064016100f0565b34801561014457600080fd5b506100f961025e565b34801561015957600080fd5b5061016261035e565b6040805192151583526020830191909152015b60405180910390f35b34801561018a57600080fd5b50610193610467565b604051908152602001610175565b3480156101ad57600080fd5b506100f9610560565b3480156101c257600080fd5b506100f9610664565b3480156101d757600080fd5b506000546040516001600160a01b039091168152602001610175565b3480156101ff57600080fd5b506100f961020e366004610d17565b610678565b34801561021f57600080fd5b5061019360015481565b34801561023557600080fd5b506100f9610244366004610d30565b610957565b34801561025557600080fd5b50610193610995565b610266610a13565b6040516370a0823160e01b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b19060009082906370a0823190602401602060405180830381865afa1580156102ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102de9190610d59565b604051630945fe5760e11b815260048101829052610dac60248201529091506001600160a01b0383169063128bfcae90604401600060405180830381600087803b15801561032b57600080fd5b505af115801561033f573d6000803e3d6000fd5b5050505060018060008282546103559190610d88565b90915550505050565b60405163842e298160e01b8152306004820152600090819073f19308f923582a6f7c465e5ce7a9dc1bec6665b1908290829063842e298190602401600060405180830381865afa1580156103b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103de9190810190610f1d565b905060005b815181101561045a578181815181106103fe576103fe610fff565b6020026020010151604001516080015165ffffffffffff1642111561044857600182828151811061043157610431610fff565b602002602001015160000151945094505050509091565b8061045281611015565b9150506103e3565b5060009485945092505050565b6000610471610a13565b6040516338ce8f2560e21b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b190819063e33a3c9490602401602060405180830381865afa1580156104c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e69190610d59565b9150811561055c57806001600160a01b0316633dda78816040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561052957600080fd5b505af115801561053d573d6000803e3d6000fd5b5050505061055c6105566000546001600160a01b031690565b83610a40565b5090565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b161060361058a6000546001600160a01b031690565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156105ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f29190610d59565b6001600160a01b0384169190610ad7565b6000546001600160a01b03166001600160a01b0316637196e8416040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561064957600080fd5b505af115801561065d573d6000803e3d6000fd5b5050505050565b61066c610a13565b6106766000610b29565b565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b1811580159061069e57506001548211155b6106dd5760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a59081cdd185ad948125160821b60448201526064016100f0565b60405162572fd560e11b8152306004820152602481018390526000906001600160a01b0383169062ae5faa9060440160c060405180830381865afa158015610729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074d919061102e565b9050806080015165ffffffffffff164210610939576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156107a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107cd9190610d59565b60405163065f146b60e11b8152600481018690529091506001600160a01b03841690630cbe28d690602401600060405180830381600087803b15801561081257600080fd5b505af1158015610826573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092508391506001600160a01b038616906370a0823190602401602060405180830381865afa158015610873573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108979190610d59565b6108a1919061104a565b90506108d46108b86000546001600160a01b031690565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b19083610ad7565b600054604051631049147760e31b8152600481018390526001600160a01b0390911690638248a3b890602401600060405180830381600087803b15801561091a57600080fd5b505af115801561092e573d6000803e3d6000fd5b505050505050505050565b6040516304301b7160e41b815260040160405180910390fd5b505050565b61095f610a13565b6001600160a01b03811661098957604051631e4fbdf760e01b8152600060048201526024016100f0565b61099281610b29565b50565b6040516338ce8f2560e21b815230600482015260009073f19308f923582a6f7c465e5ce7a9dc1bec6665b190819063e33a3c9490602401602060405180830381865afa1580156109e9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0d9190610d59565b91505090565b6000546001600160a01b031633146106765760405163118cdaa760e01b81523360048201526024016100f0565b80471015610a635760405163cd78605960e01b81523060048201526024016100f0565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610ab0576040519150601f19603f3d011682016040523d82523d6000602084013e610ab5565b606091505b505090508061095257604051630a12f52160e11b815260040160405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610952908490610b79565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610b8e6001600160a01b03841683610bdc565b90508051600014158015610bb3575080806020019051810190610bb1919061105d565b155b1561095257604051635274afe760e01b81526001600160a01b03841660048201526024016100f0565b6060610bea83836000610bf3565b90505b92915050565b606081471015610c185760405163cd78605960e01b81523060048201526024016100f0565b600080856001600160a01b03168486604051610c34919061107f565b60006040518083038185875af1925050503d8060008114610c71576040519150601f19603f3d011682016040523d82523d6000602084013e610c76565b606091505b5091509150610c86868383610c92565b925050505b9392505050565b606082610ca757610ca282610cee565b610c8b565b8151158015610cbe57506001600160a01b0384163b155b15610ce757604051639996b31560e01b81526001600160a01b03851660048201526024016100f0565b5080610c8b565b805115610cfe5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b600060208284031215610d2957600080fd5b5035919050565b600060208284031215610d4257600080fd5b81356001600160a01b0381168114610c8b57600080fd5b600060208284031215610d6b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610bed57610bed610d72565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715610dd457610dd4610d9b565b60405290565b6040516060810167ffffffffffffffff81118282101715610dd457610dd4610d9b565b604051601f8201601f1916810167ffffffffffffffff81118282101715610e2657610e26610d9b565b604052919050565b805161ffff81168114610e4057600080fd5b919050565b805165ffffffffffff81168114610e4057600080fd5b805160028110610e4057600080fd5b600060c08284031215610e7c57600080fd5b610e84610db1565b9050815172ffffffffffffffffffffffffffffffffffffff81168114610ea957600080fd5b815260208201516fffffffffffffffffffffffffffffffff81168114610ece57600080fd5b6020820152610edf60408301610e2e565b6040820152610ef060608301610e45565b6060820152610f0160808301610e45565b6080820152610f1260a08301610e5b565b60a082015292915050565b60006020808385031215610f3057600080fd5b825167ffffffffffffffff80821115610f4857600080fd5b818501915085601f830112610f5c57600080fd5b815181811115610f6e57610f6e610d9b565b610f7c848260051b01610dfd565b818152848101925060089190911b830184019087821115610f9c57600080fd5b928401925b81841015610ff4576101008489031215610fbb5760008081fd5b610fc3610dda565b8451815285850151868201526040610fdd8a828801610e6a565b908201528352610100939093019291840191610fa1565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161102757611027610d72565b5060010190565b600060c0828403121561104057600080fd5b610bea8383610e6a565b81810381811115610bed57610bed610d72565b60006020828403121561106f57600080fd5b81518015158114610c8b57600080fd5b6000825160005b818110156110a05760208186018101518583015201611086565b50600092019182525091905056fea26469706673582212203d0ba5900a621ad29b6eab364977ec7a288933d9100ffb56ec392acaaa00cca764736f6c63430008140033a2646970667358221220e7f3410382cc61a30754423a3bdbfb7d2a9e6f574003280aaa9426c3ad806b2c64736f6c63430008140033608060405234801561001057600080fd5b50338061003757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61004081610046565b50610096565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6110e4806100a56000396000f3fe6080604052600436106100955760003560e01c80638da5cb5b116100595780638da5cb5b146101cb578063a0a4a299146101f3578063cd652a1514610213578063f2fde38b14610229578063fb1de41814610249576100fb565b80633a4b66f114610138578063446d8a681461014d5780634e71d92d1461017e5780635ad3f99c146101a1578063715018a6146101b6576100fb565b366100fb573373f19308f923582a6f7c465e5ce7a9dc1bec6665b1146100f95760405162461bcd60e51b81526020600482015260146024820152730a6cadcc8cae440d2e640dcdee840a8d2e8c2dcb60631b60448201526064015b60405180910390fd5b005b60405162461bcd60e51b815260206004820152601260248201527111985b1b189858dac81d1c9a59d9d95c995960721b60448201526064016100f0565b34801561014457600080fd5b506100f961025e565b34801561015957600080fd5b5061016261035e565b6040805192151583526020830191909152015b60405180910390f35b34801561018a57600080fd5b50610193610467565b604051908152602001610175565b3480156101ad57600080fd5b506100f9610560565b3480156101c257600080fd5b506100f9610664565b3480156101d757600080fd5b506000546040516001600160a01b039091168152602001610175565b3480156101ff57600080fd5b506100f961020e366004610d17565b610678565b34801561021f57600080fd5b5061019360015481565b34801561023557600080fd5b506100f9610244366004610d30565b610957565b34801561025557600080fd5b50610193610995565b610266610a13565b6040516370a0823160e01b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b19060009082906370a0823190602401602060405180830381865afa1580156102ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102de9190610d59565b604051630945fe5760e11b815260048101829052610dac60248201529091506001600160a01b0383169063128bfcae90604401600060405180830381600087803b15801561032b57600080fd5b505af115801561033f573d6000803e3d6000fd5b5050505060018060008282546103559190610d88565b90915550505050565b60405163842e298160e01b8152306004820152600090819073f19308f923582a6f7c465e5ce7a9dc1bec6665b1908290829063842e298190602401600060405180830381865afa1580156103b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103de9190810190610f1d565b905060005b815181101561045a578181815181106103fe576103fe610fff565b6020026020010151604001516080015165ffffffffffff1642111561044857600182828151811061043157610431610fff565b602002602001015160000151945094505050509091565b8061045281611015565b9150506103e3565b5060009485945092505050565b6000610471610a13565b6040516338ce8f2560e21b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b190819063e33a3c9490602401602060405180830381865afa1580156104c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e69190610d59565b9150811561055c57806001600160a01b0316633dda78816040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561052957600080fd5b505af115801561053d573d6000803e3d6000fd5b5050505061055c6105566000546001600160a01b031690565b83610a40565b5090565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b161060361058a6000546001600160a01b031690565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156105ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f29190610d59565b6001600160a01b0384169190610ad7565b6000546001600160a01b03166001600160a01b0316637196e8416040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561064957600080fd5b505af115801561065d573d6000803e3d6000fd5b5050505050565b61066c610a13565b6106766000610b29565b565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b1811580159061069e57506001548211155b6106dd5760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a59081cdd185ad948125160821b60448201526064016100f0565b60405162572fd560e11b8152306004820152602481018390526000906001600160a01b0383169062ae5faa9060440160c060405180830381865afa158015610729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074d919061102e565b9050806080015165ffffffffffff164210610939576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156107a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107cd9190610d59565b60405163065f146b60e11b8152600481018690529091506001600160a01b03841690630cbe28d690602401600060405180830381600087803b15801561081257600080fd5b505af1158015610826573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092508391506001600160a01b038616906370a0823190602401602060405180830381865afa158015610873573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108979190610d59565b6108a1919061104a565b90506108d46108b86000546001600160a01b031690565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b19083610ad7565b600054604051631049147760e31b8152600481018390526001600160a01b0390911690638248a3b890602401600060405180830381600087803b15801561091a57600080fd5b505af115801561092e573d6000803e3d6000fd5b505050505050505050565b6040516304301b7160e41b815260040160405180910390fd5b505050565b61095f610a13565b6001600160a01b03811661098957604051631e4fbdf760e01b8152600060048201526024016100f0565b61099281610b29565b50565b6040516338ce8f2560e21b815230600482015260009073f19308f923582a6f7c465e5ce7a9dc1bec6665b190819063e33a3c9490602401602060405180830381865afa1580156109e9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0d9190610d59565b91505090565b6000546001600160a01b031633146106765760405163118cdaa760e01b81523360048201526024016100f0565b80471015610a635760405163cd78605960e01b81523060048201526024016100f0565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610ab0576040519150601f19603f3d011682016040523d82523d6000602084013e610ab5565b606091505b505090508061095257604051630a12f52160e11b815260040160405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610952908490610b79565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610b8e6001600160a01b03841683610bdc565b90508051600014158015610bb3575080806020019051810190610bb1919061105d565b155b1561095257604051635274afe760e01b81526001600160a01b03841660048201526024016100f0565b6060610bea83836000610bf3565b90505b92915050565b606081471015610c185760405163cd78605960e01b81523060048201526024016100f0565b600080856001600160a01b03168486604051610c34919061107f565b60006040518083038185875af1925050503d8060008114610c71576040519150601f19603f3d011682016040523d82523d6000602084013e610c76565b606091505b5091509150610c86868383610c92565b925050505b9392505050565b606082610ca757610ca282610cee565b610c8b565b8151158015610cbe57506001600160a01b0384163b155b15610ce757604051639996b31560e01b81526001600160a01b03851660048201526024016100f0565b5080610c8b565b805115610cfe5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b600060208284031215610d2957600080fd5b5035919050565b600060208284031215610d4257600080fd5b81356001600160a01b0381168114610c8b57600080fd5b600060208284031215610d6b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610bed57610bed610d72565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715610dd457610dd4610d9b565b60405290565b6040516060810167ffffffffffffffff81118282101715610dd457610dd4610d9b565b604051601f8201601f1916810167ffffffffffffffff81118282101715610e2657610e26610d9b565b604052919050565b805161ffff81168114610e4057600080fd5b919050565b805165ffffffffffff81168114610e4057600080fd5b805160028110610e4057600080fd5b600060c08284031215610e7c57600080fd5b610e84610db1565b9050815172ffffffffffffffffffffffffffffffffffffff81168114610ea957600080fd5b815260208201516fffffffffffffffffffffffffffffffff81168114610ece57600080fd5b6020820152610edf60408301610e2e565b6040820152610ef060608301610e45565b6060820152610f0160808301610e45565b6080820152610f1260a08301610e5b565b60a082015292915050565b60006020808385031215610f3057600080fd5b825167ffffffffffffffff80821115610f4857600080fd5b818501915085601f830112610f5c57600080fd5b815181811115610f6e57610f6e610d9b565b610f7c848260051b01610dfd565b818152848101925060089190911b830184019087821115610f9c57600080fd5b928401925b81841015610ff4576101008489031215610fbb5760008081fd5b610fc3610dda565b8451815285850151868201526040610fdd8a828801610e6a565b908201528352610100939093019291840191610fa1565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161102757611027610d72565b5060010190565b600060c0828403121561104057600080fd5b610bea8383610e6a565b81810381811115610bed57610bed610d72565b60006020828403121561106f57600080fd5b81518015158114610c8b57600080fd5b6000825160005b818110156110a05760208186018101518583015201611086565b50600092019182525091905056fea26469706673582212203d0ba5900a621ad29b6eab364977ec7a288933d9100ffb56ec392acaaa00cca764736f6c63430008140033000000000000000000000000cb6b4fc3613dcf38c19d264a0758559bebde0c4d0000000000000000000000004f5c3fdaae0b728119ed9cd57b2b32cc2f4889461ffffffffffffffffffffffffffffffffffff1234fffffffffffffffffffffff
Deployed Bytecode
0x608060405260043610620002175760003560e01c806379ba5097116200011b578063b92811c211620000a3578063ed17b5b4116200006d578063ed17b5b4146200063c578063f2fde38b1462000661578063fb1de4181462000686578063fbfa77cf146200069e576200027a565b8063b92811c214620005c0578063cbae3c1714620005d8578063d1a8580414620005fa578063e30c3978146200061c576200027a565b80638da5cb5b11620000e55780638da5cb5b14620005365780639a28b6c51462000556578063a684483d1462000590578063b4b140b514620005a8576200027a565b806379ba509714620004bc5780638096574d14620004d45780638248a3b814620004f95780638b3f7899146200051e576200027a565b806349f16525116200019f57806365291e3c116200016957806365291e3c1462000436578063673e4cbe1462000474578063715018a6146200048c5780637196e84114620004a4576200027a565b806349f1652514620003c95780634e71d92d14620003ee57806357f73ebc14620004065780635ba82b4b146200041e576200027a565b80633a4b66f111620001e15780633a4b66f114620003695780633c63f22614620003815780633d47c34b14620003995780634439131f14620003b1576200027a565b80630da996a414620002c65780631007776514620002f157806317a25931146200032c5780633929ddc21462000344576200027a565b366200027a57336000908152600f602052604090205460ff16620002785760405162461bcd60e51b815260206004820152601360248201527214d95b99195c881d5b985d5d1a1bdc9a5e9959606a1b60448201526064015b60405180910390fd5b005b3480156200028757600080fd5b5060405162461bcd60e51b815260206004820152601260248201527111985b1b189858dac81d1c9a59d9d95c995960721b60448201526064016200026f565b348015620002d357600080fd5b50620002de620006b6565b6040519081526020015b60405180910390f35b348015620002fe57600080fd5b5060075462000313906001600160a01b031681565b6040516001600160a01b039091168152602001620002e8565b3480156200033957600080fd5b50620002de600c5481565b3480156200035157600080fd5b50620002786200036336600462001c70565b62000782565b3480156200037657600080fd5b506200027862000828565b3480156200038e57600080fd5b50620002de60085481565b348015620003a657600080fd5b50620002786200095c565b348015620003be57600080fd5b50620002de62000a0f565b348015620003d657600080fd5b5062000278620003e836600462001c8a565b62000ac5565b348015620003fb57600080fd5b50620002de62000b19565b3480156200041357600080fd5b50620002de60065481565b3480156200042b57600080fd5b50620002de62000e60565b3480156200044357600080fd5b506200044e62000f13565b6040805193151584526001600160a01b03909216602084015290820152606001620002e8565b3480156200048157600080fd5b50620002de62000fe6565b3480156200049957600080fd5b5062000278620010b0565b348015620004b157600080fd5b5062000278620010c8565b348015620004c957600080fd5b506200027862001172565b348015620004e157600080fd5b5062000278620004f336600462001c8a565b620011ba565b3480156200050657600080fd5b50620002786200051836600462001c70565b62001281565b3480156200052b57600080fd5b50620002de6200132d565b3480156200054357600080fd5b506000546001600160a01b031662000313565b3480156200056357600080fd5b50620003136200057536600462001c70565b600d602052600090815260409020546001600160a01b031681565b3480156200059d57600080fd5b50620002de60095481565b348015620005b557600080fd5b50620002de600a5481565b348015620005cd57600080fd5b50620002de600b5481565b348015620005e557600080fd5b5060035462000313906001600160a01b031681565b3480156200060757600080fd5b5060045462000313906001600160a01b031681565b3480156200062957600080fd5b506001546001600160a01b031662000313565b3480156200064957600080fd5b50620002786200065b36600462001c8a565b62001357565b3480156200066e57600080fd5b50620002786200068036600462001c8a565b620013ab565b3480156200069357600080fd5b50620002de6200141f565b348015620006ab57600080fd5b50620002de60055481565b600080620006c362000fe6565b9050600073f19308f923582a6f7c465e5ce7a9dc1bec6665b190506000816001600160a01b031663af4fb7636040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200071f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000745919062001cb5565b9050806000036200075a576000935050505090565b60006200076b620f42408562001ce5565b905062000779828262001d15565b94505050505090565b6007546040805163cd652a1560e01b815290516001600160a01b03909216916103e891839163cd652a15916004808201926020929091908290030181865afa158015620007d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007f9919062001cb5565b101562000819576040516330f90d9f60e11b815260040160405180910390fd5b6200082482620014ce565b5050565b6007546040805163cd652a1560e01b815290516001600160a01b03909216916103e891839163cd652a15916004808201926020929091908290030181865afa15801562000879573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200089f919062001cb5565b10620008be5760405163807877a160e01b815260040160405180910390fd5b620008c8620010c8565b6005546000819003620008ee5760405163598172d360e11b815260040160405180910390fd5b62000907670de0b6b3a764000064174876e80062001ce5565b81106200092e576200091862001755565b620009274262093a8062001d2c565b6009555050565b600954421015620009525760405163998d019b60e01b815260040160405180910390fd5b6200091862001755565b6004546001600160a01b0316338114620009c35760405162461bcd60e51b815260206004820152602160248201527f756e617574686f72697a656420746f20616c6c6f77546974616e5374616b696e6044820152606760f81b60648201526084016200026f565b426000620009d5620151808362001d42565b620009e4906201518062001d59565b9050620009f2818362001d2c565b600881905562000a0790620d2f009062001d2c565b600955505050565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b16000818152600e6020527f9be7d1ff6ec84e99c630ebb14733fbe0ea49d80048034391f860835147bfa560546040516370a0823160e01b8152306004820152919290916370a0823190602401602060405180830381865afa15801562000a8e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000ab4919062001cb5565b62000ac0919062001d59565b905090565b62000acf62001844565b6001600160a01b03811662000af75760405163e6c4247b60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600062000b2562001873565b33321462000b46576040516348f5c3ed60e01b815260040160405180910390fd5b600047905073f19308f923582a6f7c465e5ce7a9dc1bec6665b16001600160a01b0316632277d1bd6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562000b9b57600080fd5b505af115801562000bb0573d6000803e3d6000fd5b505050506000814762000bc4919062001d59565b905060005b60065481101562000c78576000818152600d60209081526040808320548151634e71d92d60e01b815291516001600160a01b03909116938493634e71d92d9360048082019492939183900301908290875af115801562000c2d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000c53919062001cb5565b62000c5f908662001d2c565b945050808062000c6f9062001d6f565b91505062000bc9565b508260000362000c9b576040516345f8828960e11b815260040160405180910390fd5b600061271062000cae856101f462001ce5565b62000cba919062001d15565b9050600061271062000ccf61012c8762001ce5565b62000cdb919062001d15565b9050600061271062000cf0876102bc62001ce5565b62000cfc919062001d15565b90506000818362000d0e868a62001d59565b62000d1a919062001d59565b62000d26919062001d59565b6000808052600e6020527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c8054929350869290919062000d6890849062001d2c565b909155505060045462000d85906001600160a01b0316836200189c565b60035462000d9d906001600160a01b0316826200189c565b33600062000dac878662001d2c565b90508047101562000dd05760405163b7b0514d60e01b815260040160405180910390fd5b62000ddc82826200189c565b88600c600082825462000df0919062001d2c565b909155505060408051848152602081018690529081018790526060810182905289906001600160a01b038416907f9e48e42df0747083f09111bd5a1d577e07fb9ef3a10a11a77eec61699396c4f09060800160405180910390a3505050505050505062000e5d6001600255565b90565b6000805b60065481101562000f0f576000818152600d602090815260409182902054825163cd652a1560e01b815292516001600160a01b0390911692839263cd652a15926004808401938290030181865afa15801562000ec4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000eea919062001cb5565b62000ef6908462001d2c565b925050808062000f069062001d6f565b91505062000e64565b5090565b6000806000805b60065481101562000fd8576000818152600d602052604080822054815163088db14d60e31b815282516001600160a01b0390921693849390928392859263446d8a68926004808401938290030181865afa15801562000f7d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000fa3919062001da1565b91509150811562000fbe576001989397509550919350505050565b50505050808062000fcf9062001d6f565b91505062000f1a565b506000938493508392509050565b60008060005b600654811015620010aa576000818152600d602052604090819020549051639a5a6cd960e01b81526001600160a01b03909116600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b190639a5a6cd990602401602060405180830381865afa15801562001061573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001087919062001cb5565b62001093908362001d2c565b915080620010a18162001d6f565b91505062000fec565b50919050565b620010ba62001844565b620010c6600062001938565b565b6040516370a0823160e01b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b19060009082906370a0823190602401602060405180830381865afa1580156200111d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001143919062001cb5565b6001600160a01b0383166000908152600e60205260409020549091506200116b908262001d59565b6005555050565b60015433906001600160a01b03168114620011ac5760405163118cdaa760e01b81526001600160a01b03821660048201526024016200026f565b620011b78162001938565b50565b620011c462001844565b6001600160a01b0381166000908152600e60205260408120805491905580620012255760405162461bcd60e51b81526020600482015260126024820152711b9bdd1a1a5b99c81d1bc818dbdb1b1958dd60721b60448201526064016200026f565b6001600160a01b0382166200125257620008246200124b6000546001600160a01b031690565b826200189c565b816200127c6200126a6000546001600160a01b031690565b6001600160a01b038316908462001953565b505050565b3360009081526010602052604090205460ff16620012d25760405162461bcd60e51b815260206004820152600d60248201526c1b9bdd081c195c9b5a5d1d1959609a1b60448201526064016200026f565b620012dc620010c8565b80600b6000828254620012f0919062001d2c565b909155505060405181815233907f2e9913a2a8c79eba012e824e0d321123748df7744db53fd740f0498c9c68ed679060200160405180910390a250565b600061271061012c6200133f6200141f565b6200134b919062001ce5565b62000ac0919062001d15565b6200136162001844565b6001600160a01b038116620013895760405163e6c4247b60e01b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b620013b562001844565b600180546001600160a01b0383166001600160a01b03199091168117909155620013e76000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000805b60065481101562000f0f576000818152600d6020908152604091829020548251631f63bc8360e31b815292516001600160a01b0390911692839263fb1de418926004808401938290030181865afa15801562001483573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620014a9919062001cb5565b620014b5908462001d2c565b9250508080620014c59062001d6f565b91505062001423565b600060405180602001620014e29062001c62565b601f1982820381018352601f9091011660408181523060208301520160408051601f19818403018152908290526200151e929160200162001e02565b60408051601f19818403018152919052600654909150600030826200154560014362001d59565b60405160609390931b6bffffffffffffffffffffffff191660208401526034830191909152406054820152607481018590526094016040516020818303038152906040528051906020012090506000620015a882858051906020012030620019a7565b9050803b15620015f45760405162461bcd60e51b81526020600482015260166024820152754164647265737320616c72656164792065786973747360501b60448201526064016200026f565b60006200160460008487620019d1565b90506001600160a01b038116620016525760405162461bcd60e51b815260206004820152601160248201527011195c1b1bde5b595b9d0819985a5b1959607a1b60448201526064016200026f565b816001600160a01b0316816001600160a01b031614620016a85760405162461bcd60e51b815260206004820152601060248201526f082c8c8e4cae6e640dad2e6dac2e8c6d60831b60448201526064016200026f565b600780546001600160a01b0383166001600160a01b031991821681179092556000868152600d6020908152604080832080549094168517909355838252600f8152828220805460ff19908116600190811790925560109092528383208054909216179055905186917f65c6cd255dd577a3cfa318bf276e250034525393db6a9055f18c0baee6a1ba7c91a360016006600082825462001748919062001d2c565b9091555050505050505050565b6007546005805460009091556001600160a01b039091169073f19308f923582a6f7c465e5ce7a9dc1bec6665b19082906200179283838362001953565b816001600160a01b0316633a4b66f16040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620017ce57600080fd5b505af1158015620017e3573d6000803e3d6000fd5b5050505080600a6000828254620017fb919062001d2c565b90915550506040518181526001600160a01b038516907f8f8950cac73bae557ad3068a4fff5108d289ca6c6980f3eb364ccb555fec466c9060200160405180910390a250505050565b6000546001600160a01b03163314620010c65760405163118cdaa760e01b81523360048201526024016200026f565b60028054036200189657604051633ee5aeb560e01b815260040160405180910390fd5b60028055565b80471015620018c15760405163cd78605960e01b81523060048201526024016200026f565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811462001910576040519150601f19603f3d011682016040523d82523d6000602084013e62001915565b606091505b50509050806200127c57604051630a12f52160e11b815260040160405180910390fd5b600180546001600160a01b0319169055620011b78162001a5c565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526200127c90849062001aac565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b600083471015620019ff5760405163392efb2b60e21b8152476004820152602481018590526044016200026f565b815160000362001a2257604051631328927760e21b815260040160405180910390fd5b8282516020840186f590506001600160a01b03811662001a5557604051633a0ba96160e11b815260040160405180910390fd5b9392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600062001ac36001600160a01b0384168362001b16565b9050805160001415801562001aeb57508080602001905181019062001ae9919062001e23565b155b156200127c57604051635274afe760e01b81526001600160a01b03841660048201526024016200026f565b606062001b268383600062001b2f565b90505b92915050565b60608147101562001b565760405163cd78605960e01b81523060048201526024016200026f565b600080856001600160a01b0316848660405162001b74919062001e41565b60006040518083038185875af1925050503d806000811462001bb3576040519150601f19603f3d011682016040523d82523d6000602084013e62001bb8565b606091505b509150915062001bca86838362001bd4565b9695505050505050565b60608262001bed5762001be78262001c38565b62001a55565b815115801562001c0557506001600160a01b0384163b155b1562001c3057604051639996b31560e01b81526001600160a01b03851660048201526024016200026f565b508062001a55565b80511562001c495780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6111898062001e5083390190565b60006020828403121562001c8357600080fd5b5035919050565b60006020828403121562001c9d57600080fd5b81356001600160a01b038116811462001a5557600080fd5b60006020828403121562001cc857600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141762001b295762001b2962001ccf565b634e487b7160e01b600052601260045260246000fd5b60008262001d275762001d2762001cff565b500490565b8082018082111562001b295762001b2962001ccf565b60008262001d545762001d5462001cff565b500690565b8181038181111562001b295762001b2962001ccf565b60006001820162001d845762001d8462001ccf565b5060010190565b8051801515811462001d9c57600080fd5b919050565b6000806040838503121562001db557600080fd5b62001dc08362001d8b565b9150602083015190509250929050565b6000815160005b8181101562001df3576020818501810151868301520162001dd7565b50600093019283525090919050565b600062001e1b62001e14838662001dd0565b8462001dd0565b949350505050565b60006020828403121562001e3657600080fd5b62001b268262001d8b565b600062001b26828462001dd056fe608060405234801561001057600080fd5b50338061003757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61004081610046565b50610096565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6110e4806100a56000396000f3fe6080604052600436106100955760003560e01c80638da5cb5b116100595780638da5cb5b146101cb578063a0a4a299146101f3578063cd652a1514610213578063f2fde38b14610229578063fb1de41814610249576100fb565b80633a4b66f114610138578063446d8a681461014d5780634e71d92d1461017e5780635ad3f99c146101a1578063715018a6146101b6576100fb565b366100fb573373f19308f923582a6f7c465e5ce7a9dc1bec6665b1146100f95760405162461bcd60e51b81526020600482015260146024820152730a6cadcc8cae440d2e640dcdee840a8d2e8c2dcb60631b60448201526064015b60405180910390fd5b005b60405162461bcd60e51b815260206004820152601260248201527111985b1b189858dac81d1c9a59d9d95c995960721b60448201526064016100f0565b34801561014457600080fd5b506100f961025e565b34801561015957600080fd5b5061016261035e565b6040805192151583526020830191909152015b60405180910390f35b34801561018a57600080fd5b50610193610467565b604051908152602001610175565b3480156101ad57600080fd5b506100f9610560565b3480156101c257600080fd5b506100f9610664565b3480156101d757600080fd5b506000546040516001600160a01b039091168152602001610175565b3480156101ff57600080fd5b506100f961020e366004610d17565b610678565b34801561021f57600080fd5b5061019360015481565b34801561023557600080fd5b506100f9610244366004610d30565b610957565b34801561025557600080fd5b50610193610995565b610266610a13565b6040516370a0823160e01b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b19060009082906370a0823190602401602060405180830381865afa1580156102ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102de9190610d59565b604051630945fe5760e11b815260048101829052610dac60248201529091506001600160a01b0383169063128bfcae90604401600060405180830381600087803b15801561032b57600080fd5b505af115801561033f573d6000803e3d6000fd5b5050505060018060008282546103559190610d88565b90915550505050565b60405163842e298160e01b8152306004820152600090819073f19308f923582a6f7c465e5ce7a9dc1bec6665b1908290829063842e298190602401600060405180830381865afa1580156103b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103de9190810190610f1d565b905060005b815181101561045a578181815181106103fe576103fe610fff565b6020026020010151604001516080015165ffffffffffff1642111561044857600182828151811061043157610431610fff565b602002602001015160000151945094505050509091565b8061045281611015565b9150506103e3565b5060009485945092505050565b6000610471610a13565b6040516338ce8f2560e21b815230600482015273f19308f923582a6f7c465e5ce7a9dc1bec6665b190819063e33a3c9490602401602060405180830381865afa1580156104c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e69190610d59565b9150811561055c57806001600160a01b0316633dda78816040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561052957600080fd5b505af115801561053d573d6000803e3d6000fd5b5050505061055c6105566000546001600160a01b031690565b83610a40565b5090565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b161060361058a6000546001600160a01b031690565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156105ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f29190610d59565b6001600160a01b0384169190610ad7565b6000546001600160a01b03166001600160a01b0316637196e8416040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561064957600080fd5b505af115801561065d573d6000803e3d6000fd5b5050505050565b61066c610a13565b6106766000610b29565b565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b1811580159061069e57506001548211155b6106dd5760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a59081cdd185ad948125160821b60448201526064016100f0565b60405162572fd560e11b8152306004820152602481018390526000906001600160a01b0383169062ae5faa9060440160c060405180830381865afa158015610729573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074d919061102e565b9050806080015165ffffffffffff164210610939576040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156107a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107cd9190610d59565b60405163065f146b60e11b8152600481018690529091506001600160a01b03841690630cbe28d690602401600060405180830381600087803b15801561081257600080fd5b505af1158015610826573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092508391506001600160a01b038616906370a0823190602401602060405180830381865afa158015610873573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108979190610d59565b6108a1919061104a565b90506108d46108b86000546001600160a01b031690565b73f19308f923582a6f7c465e5ce7a9dc1bec6665b19083610ad7565b600054604051631049147760e31b8152600481018390526001600160a01b0390911690638248a3b890602401600060405180830381600087803b15801561091a57600080fd5b505af115801561092e573d6000803e3d6000fd5b505050505050505050565b6040516304301b7160e41b815260040160405180910390fd5b505050565b61095f610a13565b6001600160a01b03811661098957604051631e4fbdf760e01b8152600060048201526024016100f0565b61099281610b29565b50565b6040516338ce8f2560e21b815230600482015260009073f19308f923582a6f7c465e5ce7a9dc1bec6665b190819063e33a3c9490602401602060405180830381865afa1580156109e9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a0d9190610d59565b91505090565b6000546001600160a01b031633146106765760405163118cdaa760e01b81523360048201526024016100f0565b80471015610a635760405163cd78605960e01b81523060048201526024016100f0565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610ab0576040519150601f19603f3d011682016040523d82523d6000602084013e610ab5565b606091505b505090508061095257604051630a12f52160e11b815260040160405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610952908490610b79565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610b8e6001600160a01b03841683610bdc565b90508051600014158015610bb3575080806020019051810190610bb1919061105d565b155b1561095257604051635274afe760e01b81526001600160a01b03841660048201526024016100f0565b6060610bea83836000610bf3565b90505b92915050565b606081471015610c185760405163cd78605960e01b81523060048201526024016100f0565b600080856001600160a01b03168486604051610c34919061107f565b60006040518083038185875af1925050503d8060008114610c71576040519150601f19603f3d011682016040523d82523d6000602084013e610c76565b606091505b5091509150610c86868383610c92565b925050505b9392505050565b606082610ca757610ca282610cee565b610c8b565b8151158015610cbe57506001600160a01b0384163b155b15610ce757604051639996b31560e01b81526001600160a01b03851660048201526024016100f0565b5080610c8b565b805115610cfe5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b600060208284031215610d2957600080fd5b5035919050565b600060208284031215610d4257600080fd5b81356001600160a01b0381168114610c8b57600080fd5b600060208284031215610d6b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610bed57610bed610d72565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715610dd457610dd4610d9b565b60405290565b6040516060810167ffffffffffffffff81118282101715610dd457610dd4610d9b565b604051601f8201601f1916810167ffffffffffffffff81118282101715610e2657610e26610d9b565b604052919050565b805161ffff81168114610e4057600080fd5b919050565b805165ffffffffffff81168114610e4057600080fd5b805160028110610e4057600080fd5b600060c08284031215610e7c57600080fd5b610e84610db1565b9050815172ffffffffffffffffffffffffffffffffffffff81168114610ea957600080fd5b815260208201516fffffffffffffffffffffffffffffffff81168114610ece57600080fd5b6020820152610edf60408301610e2e565b6040820152610ef060608301610e45565b6060820152610f0160808301610e45565b6080820152610f1260a08301610e5b565b60a082015292915050565b60006020808385031215610f3057600080fd5b825167ffffffffffffffff80821115610f4857600080fd5b818501915085601f830112610f5c57600080fd5b815181811115610f6e57610f6e610d9b565b610f7c848260051b01610dfd565b818152848101925060089190911b830184019087821115610f9c57600080fd5b928401925b81841015610ff4576101008489031215610fbb5760008081fd5b610fc3610dda565b8451815285850151868201526040610fdd8a828801610e6a565b908201528352610100939093019291840191610fa1565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161102757611027610d72565b5060010190565b600060c0828403121561104057600080fd5b610bea8383610e6a565b81810381811115610bed57610bed610d72565b60006020828403121561106f57600080fd5b81518015158114610c8b57600080fd5b6000825160005b818110156110a05760208186018101518583015201611086565b50600092019182525091905056fea26469706673582212203d0ba5900a621ad29b6eab364977ec7a288933d9100ffb56ec392acaaa00cca764736f6c63430008140033a2646970667358221220e7f3410382cc61a30754423a3bdbfb7d2a9e6f574003280aaa9426c3ad806b2c64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000cb6b4fc3613dcf38c19d264a0758559bebde0c4d0000000000000000000000004f5c3fdaae0b728119ed9cd57b2b32cc2f4889461ffffffffffffffffffffffffffffffffffff1234fffffffffffffffffffffff
-----Decoded View---------------
Arg [0] : titanBuyAddress_ (address): 0xCb6B4Fc3613dcF38c19D264a0758559BEBDE0c4D
Arg [1] : wyvernBuyAndBurnAdddress_ (address): 0x4f5c3FdAAe0b728119ED9CD57B2b32cC2f488946
Arg [2] : deploymentKey_ (bytes32): 0x1ffffffffffffffffffffffffffffffffffff1234fffffffffffffffffffffff
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000cb6b4fc3613dcf38c19d264a0758559bebde0c4d
Arg [1] : 0000000000000000000000004f5c3fdaae0b728119ed9cd57b2b32cc2f488946
Arg [2] : 1ffffffffffffffffffffffffffffffffffff1234fffffffffffffffffffffff
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.