Contract Source Code:
File 1 of 1 : Minter
/**v3.0.1
*0xf241f4fbe146a075424beadac2d2a743ad31fb8d
*Submitted for verification at basescan.org on 2023-09-19
*/
/**v2.0.2
*0x5a5919c191bc4a72cf80bce72cdec017b392254d
*Submitted for verification at FtmScan.com on 2023-06-13
*/
/**v1.5.5
*0x56a4e8c5ff356a8cbb9312fa31fbe482ac2b9cd5
*Submitted for verification at FtmScan.com on 2023-03-31
*/
/**v1.3.15
*0xc099610fe8B7E3effbFD4D8A4C34dD4C913B63F3
*Submitted for verification at FtmScan.com on 2023-03-29
*/
/*v1.3
*0x3215E01aC5cf536f768f07d68b6E75554a450B42
*Submitted for verification at FtmScan.com on 2022-11-28
*/
/**v1.2
*0xab6904e080bdb2c2e245cdcb7d3a7901f0bc963e
*Submitted for verification at FtmScan.com on 2022-11-23
*/
/**
* EQUALIZER EXCHANGE
* The New Liquidity Hub of Fantom chain!
* https://equalizer.exchange (Dapp)
* https://discord.gg/MaMhbgHMby (Community)
*
*
*
* ChangeLog:
*
* Version: v3.0.2
* - Historical Epoch Records
* - Mints
* - Distributions
* - Price
*
* Version: v1.3.16
* - Set "New Voter" for v1.5.5
*
*
* Contributors:
* - Andre Cronje, Solidly.Exchange
* - Velodrome.finance Team
* - @smartcoding51
* - 543#3017 (Sam), ftm.guru & Equalizer.exchange
*
*
* SPDX-License-Identifier: UNLICENSED
*/
pragma solidity 0.8.9;
///pragma experimental ABIEncoderV2;
// File: contracts/interfaces/ITvlGuru.sol
//ftm.guru's Universal On-chain TVL Calculator
//Source: https://ftm.guru/docs
//Source: https://docs.ftm.guru
//Source: https://ftm.guru/rawdata/tvl
///interface ITVL {
//Using Version = v7
///function p_lpt_coin_usd(address lp) external view returns(uint256);
///function p_lpt_usd(address u,address lp) external view returns(uint256);
///function p_t_coin_usd(address lp) external view returns(uint256);
///function p_t_e_coin_usd(address lp) external view returns(uint256);
///function p_glp_usd(address m, uint256 md, bool mx, address t, uint256 td) external view returns(uint256);
///function tvlOf_glp_usd(address q, address m, uint256 md, bool mx, address t, uint256 td) external view returns(uint256);
///}
interface IPriceGuru {
function getAssetPrice(address) external view returns(uint);
}
// File: contracts/interfaces/IERC20.sol
pragma solidity 0.8.9;
interface IERC20 {
function totalSupply() external view returns (uint256);
function transfer(address recipient, uint amount) external returns (bool);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function balanceOf(address) external view returns (uint);
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
// File: contracts/interfaces/IVotingEscrow.sol
pragma solidity 0.8.9;
interface IVotingEscrow {
struct Point {
int128 bias;
int128 slope; // # -dweight / dt
uint256 ts;
uint256 blk; // block
}
function token() external view returns (address);
function team() external returns (address);
function epoch() external view returns (uint);
function point_history(uint loc) external view returns (Point memory);
function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
function user_point_epoch(uint tokenId) external view returns (uint);
function ownerOf(uint) external view returns (address);
function isApprovedOrOwner(address, uint) external view returns (bool);
function transferFrom(address, address, uint) external;
function voting(uint tokenId) external;
function abstain(uint tokenId) external;
function attach(uint tokenId) external;
function detach(uint tokenId) external;
function checkpoint() external;
function deposit_for(uint tokenId, uint value) external;
function create_lock_for(uint, uint, address) external returns (uint);
function balanceOfNFT(uint) external view returns (uint);
function totalSupply() external view returns (uint);
}
// File: contracts/interfaces/IGauge.sol
pragma solidity 0.8.9;
interface IGauge {
function notifyRewardAmount(address token, uint amount) external;
function getReward(address account, address[] memory tokens) external;
function claimFees() external returns (uint claimed0, uint claimed1);
function left(address token) external view returns (uint);
function isForPair() external view returns (bool);
}
// File: contracts/interfaces/IVoter.sol
pragma solidity 0.8.9;
interface IVoter {
function _ve() external view returns (address);
function governor() external view returns (address);
function emergencyCouncil() external view returns (address);
function attachTokenToGauge(uint _tokenId, address account) external;
function detachTokenFromGauge(uint _tokenId, address account) external;
function emitDeposit(uint _tokenId, address account, uint amount) external;
function emitWithdraw(uint _tokenId, address account, uint amount) external;
function isWhitelisted(address token) external view returns (bool);
function notifyRewardAmount(uint amount) external;
function distribute(address _gauge) external;
}
// File: contracts/interfaces/IEqual.sol
pragma solidity 0.8.9;
interface IEqual {
function totalSupply() external view returns (uint);
function balanceOf(address) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address, uint) external returns (bool);
function transferFrom(address,address,uint) external returns (bool);
function mint(address, uint) external returns (bool);
function minter() external returns (address);
}
// File: contracts/libraries/Math.sol
pragma solidity 0.8.9;
library Math {
function max(uint a, uint b) internal pure returns (uint) {
return a >= b ? a : b;
}
function min(uint a, uint b) internal pure returns (uint) {
return a < b ? a : b;
}
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function cbrt(uint256 n) internal pure returns (uint256) { unchecked {
uint256 x = 0;
for (uint256 y = 1 << 255; y > 0; y >>= 3) {
x <<= 1;
uint256 z = 3 * x * (x + 1) + 1;
if (n / y >= z) {
n -= y * z;
x += 1;
}
}
return x;
}}
}
// File: @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @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, it is bubbled up by this
* function (like regular Solidity function calls).
*
* 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.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @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`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// 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(errorMessage);
}
}
}
}
// File: @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
* initialization step. This is essential to configure modules that are added through upgrades and that require
* initialization.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
// File: contracts/Minter.sol
pragma solidity 0.8.9;
/**
* @title Minter
* @notice codifies the minting rules as per ve(3,3), abstracted from the token to support
* any token that allows minting
*/
contract Minter is Initializable {
/// @notice allows minting once per week (reset every Thursday 00:00 UTC)
uint internal constant WEEK = 86400 * 7;
uint internal constant PRECISION = 1000;
uint internal constant LOCK = 86400 * 7 * 26; /// @notice max lock period 26 weeeks
uint public TAIL_EMISSION;
uint internal emission;
uint internal numEpoch;
IEqual public _equal;
IVoter public _voter;
IVotingEscrow public _ve;
IPriceGuru public PriceGuru;
/// @notice represents a starting weekly emission of 50K EQUAL (EQUAL has 18 decimals)
uint public weekly;
uint public active_period;
address public owner;
address public team;
address public pendingTeam;
address public treasury;
uint public treasuryRate;
/// @notice Gauge address for EQUAL/WFTM pair
address public pool2;
uint public pool2Rate;
address public ms;
mapping(uint256 => uint256) public dists;
mapping(uint256 => uint256) public mints;
mapping(uint256 => uint256) public price;
/********************************************************************************************/
/*****************************************NON-STORAGE****************************************/
/********************************************************************************************/
/// NON-STORAGE
event Mint(
address indexed sender,
uint weekly,
uint circulating_supply,
uint circulating_emission,
uint balanceOf,
uint treasuryEmissions,
uint pool2Emissions,
uint priceUSD
);
event Withdrawal(
address indexed recipient,
uint amount
);
/**
* @dev initialize
* @param __voter the voting & distribution system
* @param __ve the ve(3,3) system that will be locked into
*/
function initialize(
address __voter,
address __ve,
uint256 __weekly,
IPriceGuru __pg
) public initializer {
owner = msg.sender;
team = msg.sender;
ms = msg.sender;
treasuryRate = 20;
pool2Rate = 10;
emission = 980;
TAIL_EMISSION = 2;
weekly = __weekly;
_equal = IEqual(IVotingEscrow(__ve).token());
_voter = IVoter(__voter);
_ve = IVotingEscrow(__ve);
active_period = ((block.timestamp + (2 * WEEK)) / WEEK) * WEEK;
PriceGuru = __pg;
}
/// @notice Inaugration Ceremony & Gifts
function initialSetup(
address[] memory claimants,
uint[] memory amounts
) external {
require(owner == msg.sender);
uint _max;
for (uint i = 0; i < claimants.length; i++) { _max += amounts[i]; }
_equal.mint(address(this), _max);
_equal.approve(address(_ve), type(uint).max);
for (uint i = 0; i < claimants.length; i++) {
_ve.create_lock_for(amounts[i], LOCK, claimants[i]);
}
owner = address(0);
active_period = ((block.timestamp) / WEEK) * WEEK; // allow minter.update_period() to mint new emissions THIS Thursday
}
function setTeam(address _team) external {
require(msg.sender == team, "not team");
pendingTeam = _team;
}
function acceptTeam() external {
require(msg.sender == pendingTeam, "not pending team");
team = pendingTeam;
}
function setTreasury(address _treasury) external {
require(msg.sender == team, "not team");
treasury = _treasury;
}
function setOwner(address _newo) external {
require(msg.sender == team, "not team");
owner = _newo;
}
function setTreasuryRate(uint _treasuryRate) external {
require(msg.sender == team, "not team");
require(_treasuryRate <= PRECISION, "rate too high");
treasuryRate = _treasuryRate;
}
function setPool2(address _pool2) external {
require(msg.sender == team, "not team");
require(_pool2 != address(0), "zero address");
pool2 = _pool2;
}
/// @notice weekly emission takes the max of calculated (aka target) emission versus circulating tail end emission
function weekly_emission() public view returns (uint) {
if(numEpoch < 10) return calculate_emission();
else return Math.max(calculate_emission(), circulating_emission());
}
/**
* @notice emission calculation is 0.5% of available supply to mint adjusted
* by circulating / total supply until EPOCH 104, 0.1% thereafter
*/
function calculate_emission() public view returns (uint) {
return (weekly * emission) / PRECISION;
}
/// @notice calculates tail end (infinity) emissions as 0.2% of total supply
function circulating_emission() public view returns (uint) {
return (circulating_supply() * TAIL_EMISSION) / PRECISION;
}
/// @notice calculate circulating supply as total token supply - locked supply
function circulating_supply() public view returns (uint) {
return _equal.totalSupply() - _ve.totalSupply();
}
// calculate inflation and adjust ve balances accordingly
function calculate_growth(uint _minted) public view returns (uint) {
uint _veTotal = _ve.totalSupply();
uint _equalTotal = _equal.totalSupply();
return
(((((_minted * _veTotal) / _equalTotal) * _veTotal) / _equalTotal) *
_veTotal) /
_equalTotal /
2;
}
/// v1.5.2 : Helper view functions
function epochNumber() public view returns (uint) {
return(numEpoch);
}
function decayFactor() public view returns (uint) {
return(emission);
}
/// @notice update period can only be called once per cycle (1 week)
function update_period() external returns (uint) {
uint _period = active_period;
if (block.timestamp >= _period + WEEK && owner == address(0)) { // only trigger if new week
_period = (block.timestamp / WEEK) * WEEK;
active_period = _period;
weekly = weekly_emission();
// uint _growth = calculate_growth(weekly);
uint _treasuryEmissions = (treasuryRate * weekly) / PRECISION;
uint _pool2Emissions = (pool2Rate * weekly) / PRECISION;
uint _required = weekly + _treasuryEmissions + _pool2Emissions;
uint _balanceOf = _equal.balanceOf(address(this));
if (_balanceOf < _required) {
_equal.mint(address(this), _required - _balanceOf);
}
unchecked {
++numEpoch;
}
if (numEpoch == 104) emission = PRECISION - TAIL_EMISSION;
price[ _period ] = PriceGuru.getAssetPrice( address(_equal) );
mints[ _period ] = _required - _balanceOf;
dists[ _period ] = weekly;
_equal.approve(address(_voter), weekly);
_voter.notifyRewardAmount(weekly);
{
address _treasury = treasury;
if(_treasury != address(0)) {
require(_equal.transfer(_treasury, _treasuryEmissions));
}
}{
address _pool2 = pool2;
if(_pool2 != address(0)) {
_equal.approve(_pool2, _pool2Emissions);
IGauge(_pool2).notifyRewardAmount(address(_equal), _pool2Emissions);
}
}
emit Mint(msg.sender, weekly, circulating_supply(), circulating_emission(), _balanceOf, _treasuryEmissions, _pool2Emissions, price[ _period ]);
}
return _period;
}
/// @notice withdraw remaining EQUAL tokens
function withdrawEQUAL(address _recipient) external {
require(msg.sender == team, "not team");
uint256 remaining = _equal.balanceOf(address(this));
require(remaining > 0, "No remaining tokens");
_equal.transfer(_recipient, remaining);
// Emit withdrawal event
emit Withdrawal(_recipient, remaining);
}
function reset() external {
require(msg.sender == ms, "!ms");
team = ms;
pendingTeam = ms;
treasury = ms;
}
function setNewVoter(address _v) external {
require(msg.sender == ms, "!ms");
_voter = IVoter(_v);
}
function setDecayFactor(uint _f) external {
require(msg.sender == ms, "!ms");
emission = _f;
require(weekly_emission() < circulating_supply() / 100, "too high!");
}
function setGov(address _ms) external {
require(msg.sender == ms, "!ms");
team = ms;
pendingTeam = ms;
treasury = ms;
ms = _ms;
}
function setWeeklyEmissionOverride(uint w) external {
require(msg.sender == ms, "!ms");
require(w > 3 * WEEK, "too low!");
require(w < circulating_supply() / 100, "too high!");
weekly = w;
}
}