//SPDX-License-Identifier: Delayed Release MIT
pragma solidity ^0.8.0;
/*
ERC20I (ERC20 0xInuarashi Edition)
Minified and Gas Optimized
From the efforts of the 0x Collective
https://0xcollective.net
*/
contract ERC20I {
// Token Params
string public name;
string public symbol;
constructor(string memory name_, string memory symbol_) {
name = name_;
symbol = symbol_;
}
// Decimals
uint8 public constant decimals = 18;
// Supply
uint256 public totalSupply;
// Mappings of Balances
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
// Events
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// Internal Functions
function _mint(address to_, uint256 amount_) internal {
totalSupply += amount_;
balanceOf[to_] += amount_;
emit Transfer(address(0x0), to_, amount_);
}
function _burn(address from_, uint256 amount_) internal {
balanceOf[from_] -= amount_;
totalSupply -= amount_;
emit Transfer(from_, address(0x0), amount_);
}
// Public Functions
function approve(address spender_, uint256 amount_) public virtual returns (bool) {
allowance[msg.sender][spender_] = amount_;
emit Approval(msg.sender, spender_, amount_);
return true;
}
function transfer(address to_, uint256 amount_) public virtual returns (bool) {
balanceOf[msg.sender] -= amount_;
balanceOf[to_] += amount_;
emit Transfer(msg.sender, to_, amount_);
return true;
}
function transferFrom(address from_, address to_, uint256 amount_) public virtual returns (bool) {
if (allowance[from_][msg.sender] != type(uint256).max) {
allowance[from_][msg.sender] -= amount_; }
balanceOf[from_] -= amount_;
balanceOf[to_] += amount_;
emit Transfer(from_, to_, amount_);
return true;
}
// 0xInuarashi Custom Functions
function multiTransfer(address[] memory to_, uint256[] memory amounts_) public virtual {
require(to_.length == amounts_.length, "ERC20I: To and Amounts length Mismatch!");
for (uint256 i = 0; i < to_.length; i++) {
transfer(to_[i], amounts_[i]);
}
}
function multiTransferFrom(address[] memory from_, address[] memory to_, uint256[] memory amounts_) public virtual {
require(from_.length == to_.length && from_.length == amounts_.length, "ERC20I: From, To, and Amounts length Mismatch!");
for (uint256 i = 0; i < from_.length; i++) {
transferFrom(from_[i], to_[i], amounts_[i]);
}
}
}
abstract contract ERC20IBurnable is ERC20I {
function burn(uint256 amount_) external virtual {
_burn(msg.sender, amount_);
}
function burnFrom(address from_, uint256 amount_) public virtual {
uint256 _currentAllowance = allowance[from_][msg.sender];
require(_currentAllowance >= amount_, "ERC20IBurnable: Burn amount requested exceeds allowance!");
if (allowance[from_][msg.sender] != type(uint256).max) {
allowance[from_][msg.sender] -= amount_; }
_burn(from_, amount_);
}
}
abstract contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner {
require(owner == msg.sender, "Ownable: caller is not the owner");
_;
}
function transferOwnership(address newOwner_) public virtual onlyOwner {
owner = newOwner_;
}
}
interface iMES {
struct Yield { uint40 yieldRate_; uint40 lastUpdatedTime_; uint176 pendingRewards_; }
function raw_getTotalClaimableTokens(address address_) external view returns (uint256);
function addressToYield(address address_) external view returns (Yield memory);
}
contract MartianEssence is ERC20IBurnable, Ownable {
// Constructor and Treasury Mints
constructor() ERC20I("Martian Essence", "MES") {
// Treasury Mint. We will never use this, except to benefit the community.
// This is 10M and is around 5% of the theoretical total supply.
_mint(msg.sender, 10000000 ether);
}
// Interface with Old MES
iMES public oMES = iMES(0x984b6968132DA160122ddfddcc4461C995741513);
function setoMEs(address address_) external onlyOwner {
oMES = iMES(address_);
}
// Times
uint40 public yieldStartTime = 1638619200; // 2021-12-04_07-00-00 EST
uint40 public yieldEndTime = 1956502800; // 2031-12-31_12-00-00 EST
function setYieldEndTime(uint40 yieldEndTime_) external onlyOwner {
yieldEndTime = yieldEndTime_; }
// Controllers
mapping(address => bool) public mesControllers;
function setController(address address_, bool bool_) external onlyOwner {
mesControllers[address_] = bool_; }
modifier onlyControllers {
require(mesControllers[msg.sender], "You are not a controller!"); _; }
// Yield Info
uint256 public globalModulus = (10 ** 14);
uint40 public halvingRate = 1; // This is not used
struct Yield {
uint40 yieldRate_;
uint40 lastUpdatedTime_;
uint176 pendingRewards_;
}
mapping(address => Yield) public addressToYield;
function setHalvingRate(uint40 rate_) external onlyOwner {
halvingRate = rate_; }
// Events
event Claim(address to_, uint256 amount_);
// Administration
function setYieldRate(address address_, uint256 yieldRate_) external onlyControllers {
uint40 _yieldRate = uint40(yieldRate_ / globalModulus);
addressToYield[address_].yieldRate_ = _yieldRate;
}
function addYieldRate(address address_, uint256 yieldRateAdd_) external onlyControllers {
uint40 _yieldRateAdd = uint40(yieldRateAdd_ / globalModulus);
addressToYield[address_].yieldRate_ += _yieldRateAdd;
}
function subYieldRate(address address_, uint256 yieldRateSub_) external onlyControllers {
uint40 _yieldRateSub = uint40(yieldRateSub_ / globalModulus);
addressToYield[address_].yieldRate_ -= _yieldRateSub;
}
// Credits System
function deductCredits(address address_, uint256 amount_) external onlyControllers {
uint40 _amount = uint40(amount_ / globalModulus);
require(addressToYield[address_].pendingRewards_ >= _amount, "Not enough credits!");
addressToYield[address_].pendingRewards_ -= _amount;
}
function addCredits(address address_, uint256 amount_) external onlyControllers {
uint40 _amount = uint40(amount_ / globalModulus);
addressToYield[address_].pendingRewards_ += _amount;
}
// ERC20 Burn (Stacked Functions!)
function burn(address from_, uint256 amount_) external onlyControllers {
_burn(from_, amount_);
}
// ERC20 Airdrop for Migration
function airdropMigration(address[] calldata addresses_, uint256[] calldata amounts_) external onlyOwner {
require(addresses_.length == amounts_.length,
"Array length mismatch!");
for (uint256 i = 0; i < addresses_.length; i++) {
_mint(addresses_[i], amounts_[i]);
}
}
// Migrator: Unstuck Addresses
function migrateSetNewYieldInfos(address[] calldata addresses_, uint40[] calldata lastUpdatedTimes_,
uint40[] calldata yieldRates_, uint176[] calldata pendingRewards_) external onlyOwner {
require(addresses_.length == lastUpdatedTimes_.length
&& addresses_.length == yieldRates_.length
&& addresses_.length == pendingRewards_.length,
"Array lengths mismatch!");
for (uint256 i = 0; i < addresses_.length; i++) {
addressToYield[addresses_[i]].lastUpdatedTime_ = lastUpdatedTimes_[i];
addressToYield[addresses_[i]].yieldRate_ = yieldRates_[i];
addressToYield[addresses_[i]].pendingRewards_ = pendingRewards_[i];
}
}
// Internal View Functions
function __getSmallerValueUint40(uint40 a, uint40 b) internal pure returns (uint40) {
return a < b ? a : b;
}
function __getTimestamp() internal view returns (uint40) {
return __getSmallerValueUint40(uint40(block.timestamp), yieldEndTime);
}
function __calculateYieldReward(address address_) internal view returns (uint176) {
// ~0xInuarashi: The fixed calculation code...
uint256 _totalYieldRate = uint256(addressToYield[address_].yieldRate_);
if (_totalYieldRate == 0) { return 0; }
uint256 _time = uint256(__getSmallerValueUint40(uint40(block.timestamp), yieldEndTime));
uint256 _lastUpdate = uint256(addressToYield[address_].lastUpdatedTime_);
if (_lastUpdate > yieldStartTime) {
return uint176( (_totalYieldRate * (_time - _lastUpdate) / 1 days) / halvingRate);
} else {
return 0;
}
}
// Migration Logic
bool public migrationEnabled = true;
function setMigrationEnabled(bool bool_) external onlyOwner { migrationEnabled = bool_; }
function __migrateRewards(address address_) internal {
require(migrationEnabled,
"Migration is not enabled!");
uint40 _time = __getTimestamp();
uint40 _lastUpdate = addressToYield[address_].lastUpdatedTime_;
require(_lastUpdate == 0,
"You have already migrated!");
// Set the time. This starts the yield again.
addressToYield[address_].lastUpdatedTime_ = _time;
// Claim their rewards for them from the old contract
uint176 _pendingRewards = uint176(oMES.raw_getTotalClaimableTokens(address_));
if (_pendingRewards > 0) {
addressToYield[address_].pendingRewards_ = _pendingRewards;
}
// Set their yield rate to the previous contract's yield rate
uint40 _yieldRate = oMES.addressToYield(address_).yieldRate_;
if (_yieldRate > 0) {
addressToYield[address_].yieldRate_ = _yieldRate;
}
}
function migrateRewards(address[] calldata addresses_) public {
require(migrationEnabled,
"Migration is not enabled!");
for (uint256 i = 0; i < addresses_.length; i++) {
__migrateRewards(addresses_[i]);
}
}
// Internal Write Functions
function __updateYieldReward(address address_) internal {
uint40 _time = __getSmallerValueUint40(uint40(block.timestamp), yieldEndTime);
uint40 _lastUpdate = addressToYield[address_].lastUpdatedTime_;
if (_lastUpdate > 0) {
addressToYield[address_].pendingRewards_ += __calculateYieldReward(address_);
} else {
// Migrate Rewards Logic if _lastUpdate is 0
if (migrationEnabled) {
__migrateRewards(address_);
}
}
if (_lastUpdate != yieldEndTime) {
addressToYield[address_].lastUpdatedTime_ = _time;
}
}
function __claimYieldReward(address address_) internal {
uint176 _pendingRewards = addressToYield[address_].pendingRewards_;
if (_pendingRewards > 0) {
addressToYield[address_].pendingRewards_ = 0;
uint256 _expandedReward = uint256(_pendingRewards * globalModulus);
_mint(address_, _expandedReward);
emit Claim(address_, _expandedReward);
}
}
// Public Write Functions
function updateReward(address address_) public {
__updateYieldReward(address_);
}
function claimTokens(address address_) public {
__updateYieldReward(address_);
__claimYieldReward(address_);
}
// Public Write Multi-Functions
function multiUpdateReward(address[] memory addresses_) public {
for (uint256 i = 0; i < addresses_.length; i++) {
updateReward(addresses_[i]);
}
}
function multiClaimTokens(address[] memory addresses_) public {
for (uint256 i = 0; i < addresses_.length; i++) {
claimTokens(addresses_[i]);
}
}
// Public View Functions
function getStorageClaimableTokens(address address_) public view returns (uint256) {
return uint256( uint256(addressToYield[address_].pendingRewards_) * globalModulus);
}
function getPendingClaimableTokens(address address_) public view returns (uint256) {
return uint256( uint256(__calculateYieldReward(address_)) * globalModulus);
}
function getTotalClaimableTokens(address address_) public view returns (uint256) {
return getStorageClaimableTokens(address_) + getPendingClaimableTokens(address_);
}
function getYieldRateOfAddress(address address_) public view returns (uint256) {
return uint256( uint256(addressToYield[address_].yieldRate_) * globalModulus);
}
function raw_getStorageClaimableTokens(address address_) public view returns (uint256) {
return uint256(addressToYield[address_].pendingRewards_);
}
function raw_getPendingClaimableTokens(address address_) public view returns (uint256) {
return uint256(__calculateYieldReward(address_));
}
function raw_getTotalClaimableTokens(address address_) public view returns (uint256) {
return raw_getStorageClaimableTokens(address_) + raw_getPendingClaimableTokens(address_);
}
}
{
"compilationTarget": {
"MartianEssence.sol": "MartianEssence"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"address_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"addCredits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"},{"internalType":"uint256","name":"yieldRateAdd_","type":"uint256"}],"name":"addYieldRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToYield","outputs":[{"internalType":"uint40","name":"yieldRate_","type":"uint40"},{"internalType":"uint40","name":"lastUpdatedTime_","type":"uint40"},{"internalType":"uint176","name":"pendingRewards_","type":"uint176"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"}],"name":"airdropMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"deductCredits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"getPendingClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"getStorageClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"getTotalClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"getYieldRateOfAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalModulus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"halvingRate","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mesControllers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses_","type":"address[]"}],"name":"migrateRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses_","type":"address[]"},{"internalType":"uint40[]","name":"lastUpdatedTimes_","type":"uint40[]"},{"internalType":"uint40[]","name":"yieldRates_","type":"uint40[]"},{"internalType":"uint176[]","name":"pendingRewards_","type":"uint176[]"}],"name":"migrateSetNewYieldInfos","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses_","type":"address[]"}],"name":"multiClaimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"to_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"}],"name":"multiTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"from_","type":"address[]"},{"internalType":"address[]","name":"to_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"}],"name":"multiTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses_","type":"address[]"}],"name":"multiUpdateReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oMES","outputs":[{"internalType":"contract iMES","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"raw_getPendingClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"raw_getStorageClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"raw_getTotalClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"},{"internalType":"bool","name":"bool_","type":"bool"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"rate_","type":"uint40"}],"name":"setHalvingRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"bool_","type":"bool"}],"name":"setMigrationEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"yieldEndTime_","type":"uint40"}],"name":"setYieldEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"},{"internalType":"uint256","name":"yieldRate_","type":"uint256"}],"name":"setYieldRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setoMEs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"},{"internalType":"uint256","name":"yieldRateSub_","type":"uint256"}],"name":"subYieldRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"updateReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yieldEndTime","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldStartTime","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"}]