编译器
0.8.24+commit.e11b9ed9
文件 1 的 14:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 2 的 14:ERC721Holder.sol
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../IERC721Receiver.sol";
abstract contract ERC721Holder is IERC721Receiver {
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}
文件 3 的 14:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 4 的 14:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 5 的 14:IERC404.sol
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
interface IERC404 is IERC165 {
error NotFound();
error InvalidTokenId();
error AlreadyExists();
error InvalidRecipient();
error InvalidSender();
error InvalidSpender();
error InvalidOperator();
error UnsafeRecipient();
error RecipientIsERC721TransferExempt();
error Unauthorized();
error InsufficientAllowance();
error DecimalsTooLow();
error PermitDeadlineExpired();
error InvalidSigner();
error InvalidApproval();
error OwnedIndexOverflow();
error MintLimitReached();
error InvalidExemption();
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function erc20TotalSupply() external view returns (uint256);
function erc721TotalSupply() external view returns (uint256);
function balanceOf(address owner_) external view returns (uint256);
function erc721BalanceOf(address owner_) external view returns (uint256);
function erc20BalanceOf(address owner_) external view returns (uint256);
function erc721TransferExempt(address account_) external view returns (bool);
function isApprovedForAll(
address owner_,
address operator_
) external view returns (bool);
function allowance(
address owner_,
address spender_
) external view returns (uint256);
function owned(address owner_) external view returns (uint256[] memory);
function ownerOf(uint256 id_) external view returns (address erc721Owner);
function tokenURI(uint256 id_) external view returns (string memory);
function approve(
address spender_,
uint256 valueOrId_
) external returns (bool);
function erc20Approve(
address spender_,
uint256 value_
) external returns (bool);
function erc721Approve(address spender_, uint256 id_) external;
function setApprovalForAll(address operator_, bool approved_) external;
function transferFrom(
address from_,
address to_,
uint256 valueOrId_
) external returns (bool);
function erc20TransferFrom(
address from_,
address to_,
uint256 value_
) external returns (bool);
function erc721TransferFrom(address from_, address to_, uint256 id_) external;
function transfer(address to_, uint256 amount_) external returns (bool);
function getERC721QueueLength() external view returns (uint256);
function getERC721TokensInQueue(
uint256 start_,
uint256 count_
) external view returns (uint256[] memory);
function setSelfERC721TransferExempt(bool state_) external;
function safeTransferFrom(address from_, address to_, uint256 id_) external;
function safeTransferFrom(
address from_,
address to_,
uint256 id_,
bytes calldata data_
) external;
function DOMAIN_SEPARATOR() external view returns (bytes32);
function permit(
address owner_,
address spender_,
uint256 value_,
uint256 deadline_,
uint8 v_,
bytes32 r_,
bytes32 s_
) external;
}
文件 6 的 14:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 7 的 14:IERC721Enumerable.sol
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 8 的 14:IERC721Receiver.sol
pragma solidity ^0.8.20;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 9 的 14:IStaker.sol
pragma solidity ^0.8.20;
interface IStaker {
function stake(uint256 spaceCraftId) external;
function unstake(uint256 spaceCraftId) external;
function getNextReward() external view returns (uint256);
function missionRewardAPR() external view returns (uint256 apr);
}
文件 10 的 14:IVex.sol
pragma solidity ^0.8.20;
interface IVex {
enum Rarity {
COMMON,
UNCOMMON,
RARE,
EPIC,
LEGENDARY,
MYTHICAL
}
function batchTransferERC721(
address from_,
address to_,
uint256[] calldata tokenIds
) external;
function erc721TransferByRarity(
address _from,
address _to,
uint256 _amount,
uint8 _rarity
) external;
function getHighestRarity() external returns (Rarity);
function getLowestRarity() external returns (Rarity);
function balanceOfByRarity(
address _owner,
uint8 _rarity
) external returns (uint256);
function balancesOfByRarity(
address _owner
) external returns (uint256[] memory);
function decodedTokenId(uint256 _tokenId) external;
function getRarityByTokenId(uint256 _tokenId) external;
}
文件 11 的 14:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 12 的 14:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 13 的 14:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 14 的 14:StakingV1.sol
pragma solidity ^0.8.20;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC404} from "./ERC404/interfaces/IERC404.sol";
import {IVex} from "./interfaces/IVex.sol";
import {IStaker} from "./interfaces/IStaker.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
contract StakingV1 is IStaker, ERC721Holder, Ownable, ReentrancyGuard {
using Math for uint256;
IERC721 public spacecraft;
IERC404 public vex;
address public gasStation;
address public vortex = 0x8A48b5667C8E1f0Dcd9bE1783A522bb95A59fd55;
string public planet;
uint256 public penaltyRate = 12000;
uint256 public fuelRate = 10000;
bool public stakeEnabled = false;
bool public unstakeEnabled = false;
uint256 private constant DAY_IN_SECONDS = 86400;
uint256 private constant DENOMINATOR = 1000;
uint256 public totalClaimed;
uint256 public totalBurned;
uint256 public totalMissions;
MissionsParams public params;
mapping(address => uint256[]) public stakerSpacecrafts;
mapping(uint256 => Stake) public stakedSpacecrafts;
struct Stake {
address owner;
uint256 reward;
uint256 startedAt;
uint256 estimatedEndAt;
}
struct MissionsParams {
uint256 rewardPerSpacecraft;
uint256 duration;
}
struct SpacecraftMission {
uint256 spacecraftId;
uint256 timeBeforeEnd;
uint256 currentReward;
uint256 maxReward;
}
event StartMission(address staker, uint256 spacecraftId, uint256 rewarded);
event EndMission(
address staker,
uint256 spacecraftId,
uint256 reward,
uint256 endAt
);
error FailedSpacecraftTransfer();
constructor(
address _initialOwner,
address _tokenAddress,
address _spacecraftTokenAddress,
address _gasStationAddress,
string memory _planetName
) Ownable(_initialOwner) {
vex = IERC404(_tokenAddress);
spacecraft = IERC721(_spacecraftTokenAddress);
gasStation = _gasStationAddress;
planet = _planetName;
}
receive() external payable {}
function enableStaking(bool isEnabled) external onlyOwner {
stakeEnabled = isEnabled;
}
function enableUnstaking(bool isEnabled) external onlyOwner {
unstakeEnabled = isEnabled;
}
function getTotalStakedSpacecraft() external view returns (uint256) {
return spacecraft.balanceOf(address(this));
}
function getSpacecraftsMissions(
address staker
) external view returns (SpacecraftMission[] memory) {
uint256[] memory spacecraftsId = stakerSpacecrafts[staker];
SpacecraftMission[] memory spaceMissions = new SpacecraftMission[](
spacecraftsId.length
);
if (spacecraftsId.length > 0) {
for (uint256 i = 0; i < spacecraftsId.length; i++) {
uint256 currentSpacecraftId = spacecraftsId[i];
Stake memory currentStake = stakedSpacecrafts[
currentSpacecraftId
];
uint256 timeLeft;
uint256 currentReward;
uint256 missionDistance = currentStake.startedAt +
params.duration;
if (block.timestamp < missionDistance) {
timeLeft = missionDistance - block.timestamp;
(, , uint256 burned) = _calculateMissionReward(
currentStake.reward,
currentStake.estimatedEndAt
);
currentReward = currentStake.reward > burned
? currentStake.reward - burned
: 0;
} else {
currentReward = currentStake.reward;
}
SpacecraftMission memory missionInfo = SpacecraftMission(
currentSpacecraftId,
timeLeft,
currentReward,
currentStake.reward
);
spaceMissions[i] = missionInfo;
}
}
return spaceMissions;
}
function getNextReward() external view returns (uint256) {
return _nextReward();
}
function setSpacecraftContract(address _contract) external onlyOwner {
spacecraft = IERC721(_contract);
}
function setVexContract(address _contractAddress) external onlyOwner {
vex = IERC404(_contractAddress);
}
function setGasStation(address _gasStationAddress) external onlyOwner {
gasStation = _gasStationAddress;
}
function setFuelRate(uint256 rate) external onlyOwner {
fuelRate = rate;
}
function setPenaltyRate(uint256 rate) external onlyOwner {
penaltyRate = rate;
}
function setVortex(address _blackHole) external onlyOwner {
vortex = _blackHole;
}
function withdrawETH() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No balance to withdraw");
(bool sent, ) = owner().call{value: balance}("");
require(sent, "Failed to send Ether");
}
function withdrawERC20(address token) external onlyOwner {
uint256 balance = IERC20(token).balanceOf(address(this));
require(balance > 0, "No ERC20 token to withdraw");
IERC20(token).transfer(owner(), balance);
}
function withdrawVEX() external onlyOwner {
uint256 balance = IERC404(vex).erc20BalanceOf(address(this));
if (balance > 0) {
bool success = IERC404(vex).transfer(owner(), balance);
require(success, "Withdrawal failed");
}
}
function emergencyWithdrawSpaceCraft(
address to,
uint256 spacecraftId
) external onlyOwner returns (bool) {
bool success = _transferSpacecraft(address(this), to, spacecraftId);
return success;
}
function missionRewardAPR() external view returns (uint256 apr) {
uint256 secondsInYear = DAY_IN_SECONDS * 365;
uint256 missionDurationInSeconds = params.duration;
uint256 missionReward = params.rewardPerSpacecraft;
(, uint256 rewardPerSeconds) = Math.tryDiv(
missionReward,
missionDurationInSeconds
);
(, apr) = Math.tryMul(rewardPerSeconds, secondsInYear);
return apr * 100;
}
function setMissionParams(
uint256 _rewardPerSpacecraft,
uint256 _durationInSeconds
) public onlyOwner {
require(_rewardPerSpacecraft > 0, "Reward cannot be null");
params = MissionsParams({
rewardPerSpacecraft: _rewardPerSpacecraft,
duration: _durationInSeconds
});
}
function stake(uint256 spacecraftId) public nonReentrant {
require(stakeEnabled, "Staking currently disabled");
uint256 reward = _nextReward();
require(reward > 0, "No reward in the pool");
address spacecraftOwner = spacecraft.ownerOf(spacecraftId);
require(
spacecraftOwner == _msgSender(),
"You are not the owner of the spacecraft"
);
bool successTransfer = _transferSpacecraft(
spacecraftOwner,
address(this),
spacecraftId
);
if (!successTransfer) {
revert FailedSpacecraftTransfer();
}
uint256 startAt = block.timestamp;
_updateStake(
spacecraftOwner,
spacecraftId,
reward,
startAt,
startAt + params.duration
);
_addStakerSpacecraft(spacecraftOwner, spacecraftId);
totalMissions++;
emit StartMission(spacecraftOwner, spacecraftId, startAt);
}
function unstake(uint256 spacecraftId) public nonReentrant {
require(unstakeEnabled, "Unstaking currently disabled");
address staker = _msgSender();
require(
stakedSpacecrafts[spacecraftId].owner == staker,
"You are not the owner of the spacecraft"
);
Stake memory stakeInfo = stakedSpacecrafts[spacecraftId];
(
uint256 stakerReward,
uint256 gasStationFees,
uint256 burned
) = _calculateMissionReward(stakeInfo.reward, stakeInfo.estimatedEndAt);
_updateStake(address(0), spacecraftId, 0, 0, 0);
bool successTransfer = _transferSpacecraft(
address(this),
staker,
spacecraftId
);
_unstakeVexTransfers(staker, stakerReward, gasStationFees, burned);
_removeStakerSpacecraft(staker, spacecraftId);
uint256 rewarded = stakerReward + gasStationFees;
totalClaimed += rewarded;
totalBurned += burned;
if (!successTransfer) {
revert FailedSpacecraftTransfer();
} else {
emit EndMission(staker, spacecraftId, rewarded, block.timestamp);
}
}
function _addStakerSpacecraft(
address _staker,
uint256 _spacecraftId
) internal {
stakerSpacecrafts[_staker].push(_spacecraftId);
}
function _removeStakerSpacecraft(
address _staker,
uint256 _spacecraftId
) internal {
uint256[] storage spacecraftsId = stakerSpacecrafts[_staker];
for (uint256 i = 0; i < spacecraftsId.length; i++) {
if (spacecraftsId[i] == _spacecraftId) {
spacecraftsId[i] = spacecraftsId[spacecraftsId.length - 1];
spacecraftsId.pop();
break;
}
}
}
function _updateStake(
address _staker,
uint256 _spacecraftId,
uint256 _reward,
uint256 _startedAt,
uint256 _estimatedEndAt
) internal {
Stake storage updateStake = stakedSpacecrafts[_spacecraftId];
updateStake.owner = _staker;
updateStake.reward = _reward;
updateStake.startedAt = _startedAt;
updateStake.estimatedEndAt = _estimatedEndAt;
}
function _nextReward() internal view returns (uint256) {
uint256 currentBalance = vex.erc20BalanceOf(address(this));
if (currentBalance > 0) {
if (params.rewardPerSpacecraft < currentBalance) {
return params.rewardPerSpacecraft;
} else {
return currentBalance;
}
} else {
return 0;
}
}
function _calculateMissionReward(
uint256 _reward,
uint256 _estimatedEndAt
)
public
view
virtual
returns (uint256 _stakerReward, uint256 _fuelFees, uint256 _burned)
{
uint256 _currentTime = block.timestamp;
_fuelFees = (_reward * fuelRate) / (DENOMINATOR * 100);
if (_currentTime < _estimatedEndAt) {
uint256 _timeLeft = _estimatedEndAt - _currentTime;
_burned = _abortMissionFees(_reward, _timeLeft, params.duration);
if (_burned >= _reward) {
_burned = _reward - _fuelFees;
}
uint256 _totalDeductions = _fuelFees + _burned;
_stakerReward = _totalDeductions > _reward
? 0
: _reward - _totalDeductions;
} else {
_stakerReward = _reward - _fuelFees;
_burned = 0;
}
}
function _abortMissionFees(
uint256 _reward,
uint256 _timeLeft,
uint256 _missionDuration
) public view returns (uint256) {
uint256 remainingPercentage = (_timeLeft * DENOMINATOR) /
_missionDuration;
uint256 potentialPenalty = (_reward *
remainingPercentage *
penaltyRate) / (DENOMINATOR * DENOMINATOR);
uint256 penaltyAmount = potentialPenalty > _reward
? _reward
: potentialPenalty;
return penaltyAmount;
}
function _transferSpacecraft(
address _from,
address _to,
uint256 _spacecraftId
) internal returns (bool) {
spacecraft.transferFrom(_from, _to, _spacecraftId);
address _currentOwner = spacecraft.ownerOf(_spacecraftId);
return _currentOwner == _to;
}
function _unstakeVexTransfers(
address _to,
uint256 _reward,
uint256 _fees,
uint256 _burned
) internal {
uint256 balance = vex.erc20BalanceOf(address(this));
if (balance >= _reward + _fees + _burned) {
vex.transfer(_to, _reward);
vex.transfer(gasStation, _fees);
if (_burned > 0) {
vex.transfer(vortex, _burned);
}
}
}
}
{
"compilationTarget": {
"contracts/StakingV1.sol": "StakingV1"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_spacecraftTokenAddress","type":"address"},{"internalType":"address","name":"_gasStationAddress","type":"address"},{"internalType":"string","name":"_planetName","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FailedSpacecraftTransfer","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"spacecraftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endAt","type":"uint256"}],"name":"EndMission","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":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"spacecraftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewarded","type":"uint256"}],"name":"StartMission","type":"event"},{"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"},{"internalType":"uint256","name":"_timeLeft","type":"uint256"},{"internalType":"uint256","name":"_missionDuration","type":"uint256"}],"name":"_abortMissionFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"},{"internalType":"uint256","name":"_estimatedEndAt","type":"uint256"}],"name":"_calculateMissionReward","outputs":[{"internalType":"uint256","name":"_stakerReward","type":"uint256"},{"internalType":"uint256","name":"_fuelFees","type":"uint256"},{"internalType":"uint256","name":"_burned","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"spacecraftId","type":"uint256"}],"name":"emergencyWithdrawSpaceCraft","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isEnabled","type":"bool"}],"name":"enableStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isEnabled","type":"bool"}],"name":"enableUnstaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fuelRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasStation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNextReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getSpacecraftsMissions","outputs":[{"components":[{"internalType":"uint256","name":"spacecraftId","type":"uint256"},{"internalType":"uint256","name":"timeBeforeEnd","type":"uint256"},{"internalType":"uint256","name":"currentReward","type":"uint256"},{"internalType":"uint256","name":"maxReward","type":"uint256"}],"internalType":"struct StakingV1.SpacecraftMission[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalStakedSpacecraft","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"missionRewardAPR","outputs":[{"internalType":"uint256","name":"apr","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"params","outputs":[{"internalType":"uint256","name":"rewardPerSpacecraft","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"planet","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setFuelRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gasStationAddress","type":"address"}],"name":"setGasStation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardPerSpacecraft","type":"uint256"},{"internalType":"uint256","name":"_durationInSeconds","type":"uint256"}],"name":"setMissionParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setPenaltyRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"setSpacecraftContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contractAddress","type":"address"}],"name":"setVexContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_blackHole","type":"address"}],"name":"setVortex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spacecraft","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"spacecraftId","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakedSpacecrafts","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"estimatedEndAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakerSpacecrafts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBurned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalMissions","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":[{"internalType":"uint256","name":"spacecraftId","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakeEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vex","outputs":[{"internalType":"contract IERC404","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vortex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawVEX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]