Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Ownable} from "@openzeppelin/access/Ownable.sol";
import {EnumerableSetLib} from "solady/utils/EnumerableSetLib.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {IBigcoin} from "./interfaces/IBigcoin.sol";
import {Miner} from "./types/Miner.sol";
import {Facility} from "./types/Facility.sol";
import {NewFacility} from "./types/NewFacility.sol";
import {Errors} from "./libraries/Errors.sol";
import {Events} from "./libraries/Events.sol";
// Bigcoin main program
contract Main is Ownable {
using EnumerableSetLib for EnumerableSetLib.Uint256Set;
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// STORAGE
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/// @dev Bigcoin token address.
address public bigcoin;
/// @dev bigtoshi fee recipient.
address public bigtoshi;
/// @dev Mining start block.
uint256 public startBlock;
bool public miningHasStarted;
// every miner has an id
uint256 universalMinerId;
/// @dev Total unique miners.
uint256 public uniqueMinerCount;
/// @dev Total facilities.
uint256 public facilityCount;
/// @dev Tracks last time we updated rewards.
uint256 public lastRewardBlock;
/// @dev Total network hashrate.
uint256 public totalHashrate;
/// @dev Tracks bigcoin per single hash.
uint256 public cumulativeBigcoinPerHash;
/// @dev Initial cooldown time is 24H.
uint256 public cooldown = 24 hours;
/// @dev Initial referral fee is 2.5%.
uint256 public referralFee = 0.025e18;
/// @dev Initial burn is 75%.
uint256 public burnPct = 0.75e18;
/// @dev Players total hashrate.
mapping(address => uint256) public playerHashrate;
/// @dev Players pending rewards.
mapping(address => uint256) public playerPendingRewards;
/// @dev Players debt (updated after rewards are updated for a player).
mapping(address => uint256) public playerBigcoinDebt;
/// @dev Tracks different miners and its stats.
mapping(uint256 => Miner) public miners;
/// @dev Tracks different facilities and its stats.
mapping(uint256 => NewFacility) public facilities;
/// @dev Tracks how many of each miner a player has. Only tracks Id.
mapping(address => EnumerableSetLib.Uint256Set) public playerMinersOwned;
/// @dev Maps minerId to the actual miner struct
mapping(uint256 => Miner) public playerMinersId;
/// @dev Tracks players facility stats.
mapping(address => Facility) public ownerToFacility;
/// @dev Tracks new players initializing in the game.
mapping(address => bool) public initializedStarterFacility;
/// @dev Tracks if a player has acquired a free miner.
mapping(address => bool) public acquiredStarterMiner;
/// @dev Tracks if a there's a secondary market for a certain miner.
mapping(uint256 => uint256) public minerSecondHandMarket;
/// @dev Tracks each miners coords for a player
mapping(address => mapping(uint256 => mapping(uint256 => bool))) public playerOccupiedCoords;
mapping(address => uint256) public lastFacilityUpgradeTimestamp;
/// @dev Tracks referrals.
mapping(address => address) public referrals;
/// @dev Tracks referred users.
mapping(address => address[]) public referredUsers;
/// @dev Track how much each referrer got paid.
mapping(address => uint256) public referralBonusPaid;
/// @dev Purchasing initial facility price in ETH.
uint256 public initialFacilityPrice = 0.005 ether;
/// @dev start miner and facility index (these will never change since every player starts with same facility).
uint256 public immutable STARTER_MINER_INDEX;
uint256 public immutable STARTER_FACILITY_INDEX;
/// @dev Halvening every 4,200,000 blocks, roughly 50 days
uint256 public constant HALVING_INTERVAL = 4_200_000;
/// @dev Initial bigcoin per block
uint256 public constant INITIAL_BIGCOIN_PER_BLOCK = 2.5e18; // 2.5 Bigcoin per block
uint256 public constant REWARDS_PRECISION = 1e18;
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// CONSTRUCTOR
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
constructor() Ownable(msg.sender) {
STARTER_MINER_INDEX = ++uniqueMinerCount;
// add starter free miner
miners[STARTER_MINER_INDEX] = Miner(
STARTER_MINER_INDEX,
type(uint256).max,
type(uint256).max,
type(uint256).max,
100,
1,
type(uint256).max, // this is the free starter miner, people cannot buy it
false
);
// add starter facility
facilities[++facilityCount] = NewFacility(
4,
28,
type(uint256).max, // starter facility, people cannot buy it
false,
2,
2
);
STARTER_FACILITY_INDEX = facilityCount;
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// USER FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev All new players must purchase an initial facility with ETH.
*
* Players can only purchase new facilities after acquiring initial Facility.
*/
function purchaseInitialFacility(address referrer) external payable {
if (msg.value != initialFacilityPrice) {
revert Errors.IncorrectValue();
}
if (initializedStarterFacility[msg.sender]) {
revert Errors.AlreadyPurchasedInitialFactory();
}
if (referrer == msg.sender) {
revert Errors.InvalidReferrer();
}
initializedStarterFacility[msg.sender] = true;
if (referrer != address(0)) {
referrals[msg.sender] = referrer;
referredUsers[referrer].push(msg.sender);
}
NewFacility memory newFacility = facilities[STARTER_FACILITY_INDEX];
Facility storage facility = ownerToFacility[msg.sender];
// initialize players starter facility
facility.facilityIndex = STARTER_FACILITY_INDEX;
facility.maxMiners = newFacility.maxMiners;
facility.totalPowerOutput = newFacility.totalPowerOutput;
facility.x = newFacility.x;
facility.y = newFacility.y;
emit Events.InitialFacilityPurchased(msg.sender);
}
/**
* @dev All new players can redeem 1 free miner.
*
* Players are not required to redeem a free miner.
*/
function getFreeStarterMiner(uint256 x, uint256 y) external {
if (acquiredStarterMiner[msg.sender]) {
revert Errors.StarterMinerAlreadyAcquired();
}
acquiredStarterMiner[msg.sender] = true;
Miner memory miner = miners[STARTER_MINER_INDEX];
Facility storage facility = ownerToFacility[msg.sender];
if (_isInvalidCoordinates(x, y, facility.x, facility.y)) {
revert Errors.InvalidMinerCoordinates();
}
if (facility.currPowerOutput + miner.powerConsumption > facility.totalPowerOutput) {
revert Errors.FacilityInadequatePowerOutput();
}
miner.x = x;
miner.y = y;
miner.id = ++universalMinerId;
playerOccupiedCoords[msg.sender][x][y] = true;
playerMinersOwned[msg.sender].add(universalMinerId);
playerMinersId[universalMinerId] = miner;
// increase facility miners
facility.currMiners++;
// increase power consumption from factory
facility.currPowerOutput += miner.powerConsumption;
// emit Events.FreeMinerRedeemed(msg.sender);
emit Events.MinerBought(msg.sender, STARTER_MINER_INDEX, 0, universalMinerId, x, y);
_increaseHashrate(msg.sender, miner.hashrate);
}
/**
* @dev Purchase new miners using bigcoin.
*/
function buyMiner(uint256 minerIndex, uint256 x, uint256 y) external {
// check user has enough funds for miner and facility allows it
Miner memory miner = miners[minerIndex];
Facility storage facility = ownerToFacility[msg.sender];
if (_isInvalidCoordinates(x, y, facility.x, facility.y)) {
revert Errors.InvalidMinerCoordinates();
}
// this case will cover if an index too large is passed in
if (!miner.inProduction) revert Errors.MinerNotInProduction();
if (IBigcoin(bigcoin).balanceOf(msg.sender) < miner.cost) {
revert Errors.TooPoor();
}
if (facility.currPowerOutput + miner.powerConsumption > facility.totalPowerOutput) {
revert Errors.FacilityInadequatePowerOutput();
}
// transfer correct amt of bigcoin
IBigcoin(bigcoin).transferFrom(msg.sender, address(this), miner.cost);
// burn
IBigcoin(bigcoin).burn(FixedPointMathLib.mulWad(miner.cost, burnPct));
miner.x = x;
miner.y = y;
miner.id = ++universalMinerId;
playerOccupiedCoords[msg.sender][x][y] = true;
playerMinersOwned[msg.sender].add(universalMinerId);
playerMinersId[universalMinerId] = miner;
// increase facility miners
facility.currMiners++;
// increase power consumption from factory
facility.currPowerOutput += miner.powerConsumption;
emit Events.MinerBought(msg.sender, minerIndex, miner.cost, universalMinerId, x, y);
_increaseHashrate(msg.sender, miner.hashrate);
}
/**
* @dev Sell miners, if there's a secondary market, player will get bigcoin in return.
*
* Players will sell miners in the case they run out of spots in their facility to put new miners.
*/
function sellMiner(uint256 minerId) external {
if (!playerMinersOwned[msg.sender].contains(minerId)) {
revert Errors.PlayerDoesNotOwnMiner();
}
Miner memory miner = playerMinersId[minerId];
uint256 secondHandPrice = minerSecondHandMarket[miner.minerIndex];
// if there's not enough bigcoin in this contract to pay out, revert
if (secondHandPrice > IBigcoin(bigcoin).balanceOf(address(this))) {
revert Errors.GreatDepression();
}
Facility storage facility = ownerToFacility[msg.sender];
// decrease facility miners
facility.currMiners--;
// decrease power consumption from factory
facility.currPowerOutput -= miner.powerConsumption;
playerMinersOwned[msg.sender].remove(minerId);
delete playerMinersId[minerId];
playerOccupiedCoords[msg.sender][miner.x][miner.y] = false;
emit Events.MinerSold(msg.sender, miner.minerIndex, secondHandPrice, minerId, miner.x, miner.y);
_decreaseHashrate(msg.sender, miner.hashrate);
if (secondHandPrice > 0) {
IBigcoin(bigcoin).transfer(msg.sender, secondHandPrice);
}
}
/**
* @dev Purchase larger or more power output facility.
*
* Players must linearly climb the facilities.
*/
function buyNewFacility() external {
// need to purchase initial facility first
if (!initializedStarterFacility[msg.sender]) {
revert Errors.NeedToInitializeFacility();
}
Facility storage currFacility = ownerToFacility[msg.sender];
uint256 currFacilityIndex = currFacility.facilityIndex;
if (currFacilityIndex == facilityCount) {
revert Errors.AlreadyAtMaxFacility();
}
// 24H cooldown between each facility upgrade
if (block.timestamp - lastFacilityUpgradeTimestamp[msg.sender] < cooldown) {
revert Errors.CantBuyNewFacilityYet();
}
NewFacility memory newFacility = facilities[currFacilityIndex + 1];
if (!newFacility.inProduction) {
revert Errors.NewFacilityNotInProduction();
}
if (IBigcoin(bigcoin).balanceOf(msg.sender) < newFacility.cost) {
revert Errors.TooPoor();
}
IBigcoin(bigcoin).transferFrom(msg.sender, address(this), newFacility.cost);
// burn
IBigcoin(bigcoin).burn(FixedPointMathLib.mulWad(newFacility.cost, burnPct));
currFacility.facilityIndex++;
currFacility.maxMiners = newFacility.maxMiners;
currFacility.totalPowerOutput = newFacility.totalPowerOutput;
currFacility.x = newFacility.x;
currFacility.y = newFacility.y;
lastFacilityUpgradeTimestamp[msg.sender] = block.timestamp;
emit Events.FacilityBought(msg.sender, currFacility.facilityIndex, newFacility.cost);
}
/**
* @dev Claim rewards for player.
*/
function claimRewards() external {
_updateRewards(msg.sender);
uint256 rewards = playerPendingRewards[msg.sender];
if (rewards == 0) {
revert Errors.NoRewardsPending();
}
playerPendingRewards[msg.sender] = 0;
// referral bonus
uint256 referralBonus = FixedPointMathLib.mulWad(rewards, referralFee);
uint256 finalRewards = rewards - referralBonus;
IBigcoin(bigcoin).mint(msg.sender, finalRewards);
address referrer = referrals[msg.sender];
if (referrer != address(0)) {
IBigcoin(bigcoin).mint(referrer, referralBonus);
referralBonusPaid[referrer] += referralBonus;
} else {
// else mint referral fee to this contract
IBigcoin(bigcoin).mint(address(this), referralBonus);
referralBonusPaid[address(this)] += referralBonus;
}
emit Events.RewardsClaimed(msg.sender, rewards);
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// INTERNAL FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev Update global cumulative rewards before game state changes.
*/
function _updateCumulativeRewards() internal {
if (totalHashrate == 0) {
lastRewardBlock = block.number;
return;
}
// if we have not updated rewards and a halvening occurs, we must add the different
// bigcoin emissions per halvening
uint256 currentBlock = lastRewardBlock;
uint256 lastBigcoinPerBlock =
INITIAL_BIGCOIN_PER_BLOCK / (2 ** ((lastRewardBlock - startBlock) / HALVING_INTERVAL));
while (currentBlock < block.number) {
uint256 nextHalvingBlock =
(startBlock % HALVING_INTERVAL) + ((currentBlock / HALVING_INTERVAL) + 1) * HALVING_INTERVAL;
uint256 endBlock = (nextHalvingBlock < block.number) ? nextHalvingBlock : block.number;
// apply correct emission rate for this segment
cumulativeBigcoinPerHash +=
((lastBigcoinPerBlock * (endBlock - currentBlock) * REWARDS_PRECISION) / totalHashrate);
// move to next segment
currentBlock = endBlock;
// if a halving has happened, apply the new rate
if (currentBlock == nextHalvingBlock) {
lastBigcoinPerBlock /= 2;
}
}
lastRewardBlock = block.number;
}
function _updateRewards(address player) internal {
_updateCumulativeRewards();
playerPendingRewards[player] +=
(playerHashrate[player] * (cumulativeBigcoinPerHash - playerBigcoinDebt[player])) / REWARDS_PRECISION;
playerBigcoinDebt[player] = cumulativeBigcoinPerHash;
}
/**
* @dev Increase players hashrate.
*/
function _increaseHashrate(address player, uint256 hashrate) internal {
// only start rewarding bigcoin per block when the first miner is added
if (!miningHasStarted) {
miningHasStarted = true;
startBlock = block.number;
lastRewardBlock = block.number;
emit Events.MiningStarted(startBlock);
}
// update rewards and global state before modifying players state
_updateRewards(player);
totalHashrate += hashrate;
playerHashrate[player] += hashrate;
emit Events.PlayerHashrateIncreased(msg.sender, playerHashrate[player], playerPendingRewards[player]);
}
/**
* @dev Decrease players hashrate.
*/
function _decreaseHashrate(address player, uint256 hashrate) internal {
// update rewards and global state before modifying players state
_updateRewards(player);
totalHashrate -= hashrate;
playerHashrate[player] -= hashrate;
emit Events.PlayerHashrateDecreased(msg.sender, playerHashrate[player], playerPendingRewards[player]);
}
/**
* @dev checks the miners x,y coords are within bounds and there isn't an existent miner at that position.
*/
function _isInvalidCoordinates(uint256 desiredX, uint256 desiredY, uint256 facilityX, uint256 facilityY)
internal
view
returns (bool)
{
if (desiredX >= facilityX || desiredY >= facilityY) {
return true;
}
return playerOccupiedCoords[msg.sender][desiredX][desiredY];
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// VIEW FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev Get the current mining emission rate based on halvings
*/
function getBigcoinPerBlock() public view returns (uint256) {
if (!miningHasStarted) {
return 0;
}
uint256 halvingsSinceStart = (block.number - startBlock) / HALVING_INTERVAL;
return INITIAL_BIGCOIN_PER_BLOCK / (2 ** halvingsSinceStart);
}
/**
* @dev Get current pending rewards for player.
*/
function pendingRewards(address player) external view returns (uint256) {
// below is basically the same logic as _updateCumulativeRewards() but doesnt change state
if (!miningHasStarted) {
return 0;
}
if (totalHashrate == 0) {
return FixedPointMathLib.mulWad(playerPendingRewards[player], 1e18 - referralFee);
}
uint256 currentBlock = lastRewardBlock;
uint256 lastBigcoinPerBlock =
INITIAL_BIGCOIN_PER_BLOCK / (2 ** ((lastRewardBlock - startBlock) / HALVING_INTERVAL));
uint256 simulatedCumulativeBigcoinPerHash = cumulativeBigcoinPerHash;
while (currentBlock < block.number) {
uint256 nextHalvingBlock =
(startBlock % HALVING_INTERVAL) + ((currentBlock / HALVING_INTERVAL) + 1) * HALVING_INTERVAL;
uint256 endBlock = (nextHalvingBlock < block.number) ? nextHalvingBlock : block.number;
// simulate rewards accumulation
if (totalHashrate > 0) {
simulatedCumulativeBigcoinPerHash +=
((lastBigcoinPerBlock * (endBlock - currentBlock) * REWARDS_PRECISION) / totalHashrate);
}
// move to next segment
currentBlock = endBlock;
// if we reached a halving point, apply the new emission rate
if (currentBlock == nextHalvingBlock) {
lastBigcoinPerBlock /= 2;
}
}
return FixedPointMathLib.mulWad(
playerPendingRewards[player]
+ (
(playerHashrate[player] * (simulatedCumulativeBigcoinPerHash - playerBigcoinDebt[player]))
/ REWARDS_PRECISION
),
1e18 - referralFee
);
}
/**
* @dev Get current bigcoin a player mines per block.
*/
function playerBigcoinPerBlock(address player) external view returns (uint256) {
if (totalHashrate == 0) {
return 0; // Prevent division by zero
}
uint256 currBigcoinPerBlock =
INITIAL_BIGCOIN_PER_BLOCK / (2 ** ((block.number - startBlock) / HALVING_INTERVAL));
return FixedPointMathLib.mulDiv(playerHashrate[player], currBigcoinPerBlock, totalHashrate);
}
/**
* @dev Get blocks till next halvening.
*/
function blocksUntilNextHalving() external view returns (uint256) {
if (startBlock == 0) revert Errors.MiningHasntStarted();
uint256 nextHalvingBlock =
(startBlock % HALVING_INTERVAL) + ((block.number / HALVING_INTERVAL) + 1) * HALVING_INTERVAL;
return nextHalvingBlock - block.number;
}
/**
* @dev Get next available facility upgrade time.
*/
function timeUntilNextFacilityUpgrade(address player) external view returns (uint256) {
if (lastFacilityUpgradeTimestamp[player] + cooldown < block.timestamp) {
return 0;
}
return (lastFacilityUpgradeTimestamp[player] + cooldown) - block.timestamp;
}
/**
* @dev Get all miners for a player.
*/
function getPlayerMinersPaginated(address player, uint256 startIndex, uint256 size)
external
view
returns (Miner[] memory)
{
EnumerableSetLib.Uint256Set storage set = playerMinersOwned[player];
uint256 length = set.length();
// Return empty array if start index is beyond array bounds
if (startIndex >= length) {
return new Miner[](0);
}
// Calculate how many items we can actually return
uint256 remaining = length - startIndex;
uint256 returnSize = size > remaining ? remaining : size;
Miner[] memory playerMiners = new Miner[](returnSize);
for (uint256 i = 0; i < returnSize; i++) {
playerMiners[i] = playerMinersId[set.at(startIndex + i)];
}
return playerMiners;
}
/**
* @dev Get all referred users.
*/
function getReferrals(address referrer) external view returns (address[] memory) {
return referredUsers[referrer];
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// OWNER FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev Add new miner to game.
*/
function addMiner(uint256 hashrate, uint256 powerConsumption, uint256 cost, bool inProduction) external onlyOwner {
// must increment first cause compiler evaluates function arg before evaluating the assignment
++uniqueMinerCount;
miners[uniqueMinerCount] = Miner(uniqueMinerCount, 0, 0, 0, hashrate, powerConsumption, cost, inProduction);
emit Events.NewMinerAdded(uniqueMinerCount, hashrate, powerConsumption, cost, inProduction);
}
/**
* @dev Allows or prevents miner from being purchased.
*
* Miners cannot be removed, only toggled off from purchased.
*/
function toggleMinerProduction(uint256 minerIndex, bool inProduction) external onlyOwner {
if (minerIndex < STARTER_MINER_INDEX || minerIndex > uniqueMinerCount) {
revert Errors.InvalidMinerIndex();
}
Miner storage miner = miners[minerIndex];
miner.inProduction = inProduction;
emit Events.MinerProductionToggled(minerIndex, inProduction);
}
/**
* @dev Add new facility to game.
*/
function addFacility(
uint256 maxMiners,
uint256 totalPowerOutput,
uint256 cost,
bool inProduction,
uint256 x,
uint256 y
) external onlyOwner {
if (x * y != maxMiners) {
revert Errors.FacilityDimensionsInvalid();
}
if (facilities[facilityCount].x > x || facilities[facilityCount].y > y) {
revert Errors.FacilityDimensionsInvalid();
}
if (facilities[facilityCount].totalPowerOutput > totalPowerOutput) {
revert Errors.InvalidPowerOutput();
}
facilities[++facilityCount] = NewFacility(maxMiners, totalPowerOutput, cost, inProduction, x, y);
emit Events.NewFacilityAdded(facilityCount, totalPowerOutput, cost, inProduction, x, y);
}
/**
* @dev Allows or prevents new facilities from being purchased.
*
* New facilities cannot be removed, only toggled off from purchased.
*/
function toggleFacilityProduction(uint256 facilityIndex, bool inProduction) external onlyOwner {
if (facilityIndex < STARTER_FACILITY_INDEX || facilityIndex > facilityCount) {
revert Errors.InvalidFacilityIndex();
}
NewFacility storage facility = facilities[facilityIndex];
facility.inProduction = inProduction;
emit Events.FacilityProductionToggled(facilityIndex, inProduction);
}
/**
* @dev Add secondary market for a miner.
*/
function addSecondaryMarketForMiner(uint256 minerIndex, uint256 price) external onlyOwner {
minerSecondHandMarket[minerIndex] = price;
emit Events.MinerSecondaryMarketAdded(minerIndex, price);
}
function setBigcoin(address _bigcoin) external onlyOwner {
bigcoin = _bigcoin;
}
function setBigtoshi(address _bigtoshi) external onlyOwner {
bigtoshi = _bigtoshi;
}
function setInitialFacilityPrice(uint256 _initialPrice) external onlyOwner {
initialFacilityPrice = _initialPrice;
}
function setReferralFee(uint256 fee) external onlyOwner {
if (fee > 1e18) revert Errors.InvalidFee();
referralFee = fee;
}
function setBurnPct(uint256 burn) external onlyOwner {
if (burn > 1e18) revert Errors.InvalidFee();
burnPct = burn;
}
function setCooldown(uint256 _cooldown) external onlyOwner {
cooldown = _cooldown;
}
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
(bool success,) = payable(bigtoshi).call{value: balance}("");
if (!success) revert Errors.WithdrawFailed();
}
function withdrawBigcoin(uint256 amt) external onlyOwner {
IBigcoin(bigcoin).transfer(bigtoshi, amt);
}
function changeMinerCost(uint256 minerIndex, uint256 newCost) external onlyOwner {
if (minerIndex > uniqueMinerCount) {
revert Errors.NonExistentMiner();
}
if (minerIndex == STARTER_MINER_INDEX) {
revert Errors.CantModifyStarterMiner();
}
Miner storage miner = miners[minerIndex];
miner.cost = newCost;
emit Events.MinerCostChanged(minerIndex, newCost);
}
function changeFacilityCost(uint256 facilityIndex, uint256 newCost) external onlyOwner {
if (facilityIndex > facilityCount) {
revert Errors.NonExistentFacility();
}
if (facilityIndex == STARTER_FACILITY_INDEX) {
revert Errors.CantModifyStarterFacility();
}
NewFacility storage facility = facilities[facilityIndex];
facility.cost = newCost;
emit Events.FacilityCostChanged(facilityIndex, newCost);
}
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// 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);
}
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for managing enumerable sets in storage.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EnumerableSetLib.sol)
///
/// @dev Note:
/// In many applications, the number of elements in an enumerable set is small.
/// This enumerable set implementation avoids storing the length and indices
/// for up to 3 elements. Once the length exceeds 3 for the first time, the length
/// and indices will be initialized. The amortized cost of adding elements is O(1).
///
/// The AddressSet implementation packs the length with the 0th entry.
///
/// All enumerable sets except Uint8Set use a pop and swap mechanism to remove elements.
/// This means that the iteration order of elements can change between element removals.
library EnumerableSetLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The index must be less than the length.
error IndexOutOfBounds();
/// @dev The value cannot be the zero sentinel.
error ValueIsZeroSentinel();
/// @dev Cannot accommodate a new unique value with the capacity.
error ExceedsCapacity();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A sentinel value to denote the zero value in storage.
/// No elements can be equal to this value.
/// `uint72(bytes9(keccak256(bytes("_ZERO_SENTINEL"))))`.
uint256 private constant _ZERO_SENTINEL = 0xfbb67fda52d4bfb8bf;
/// @dev The storage layout is given by:
/// ```
/// mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
/// mstore(0x00, set.slot)
/// let rootSlot := keccak256(0x00, 0x24)
/// mstore(0x20, rootSlot)
/// mstore(0x00, shr(96, shl(96, value)))
/// let positionSlot := keccak256(0x00, 0x40)
/// let valueSlot := add(rootSlot, sload(positionSlot))
/// let valueInStorage := shr(96, sload(valueSlot))
/// let lazyLength := shr(160, shl(160, sload(rootSlot)))
/// ```
uint256 private constant _ENUMERABLE_ADDRESS_SET_SLOT_SEED = 0x978aab92;
/// @dev The storage layout is given by:
/// ```
/// mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
/// mstore(0x00, set.slot)
/// let rootSlot := keccak256(0x00, 0x24)
/// mstore(0x20, rootSlot)
/// mstore(0x00, value)
/// let positionSlot := keccak256(0x00, 0x40)
/// let valueSlot := add(rootSlot, sload(positionSlot))
/// let valueInStorage := sload(valueSlot)
/// let lazyLength := sload(not(rootSlot))
/// ```
uint256 private constant _ENUMERABLE_WORD_SET_SLOT_SEED = 0x18fb5864;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev An enumerable address set in storage.
struct AddressSet {
uint256 _spacer;
}
/// @dev An enumerable bytes32 set in storage.
struct Bytes32Set {
uint256 _spacer;
}
/// @dev An enumerable uint256 set in storage.
struct Uint256Set {
uint256 _spacer;
}
/// @dev An enumerable int256 set in storage.
struct Int256Set {
uint256 _spacer;
}
/// @dev An enumerable uint8 set in storage. Useful for enums.
struct Uint8Set {
uint256 data;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GETTERS / SETTERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of elements in the set.
function length(AddressSet storage set) internal view returns (uint256 result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let rootPacked := sload(rootSlot)
let n := shr(160, shl(160, rootPacked))
result := shr(1, n)
for {} iszero(or(iszero(shr(96, rootPacked)), n)) {} {
result := 1
if iszero(sload(add(rootSlot, result))) { break }
result := 2
if iszero(sload(add(rootSlot, result))) { break }
result := 3
break
}
}
}
/// @dev Returns the number of elements in the set.
function length(Bytes32Set storage set) internal view returns (uint256 result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let n := sload(not(rootSlot))
result := shr(1, n)
for {} iszero(n) {} {
result := 0
if iszero(sload(add(rootSlot, result))) { break }
result := 1
if iszero(sload(add(rootSlot, result))) { break }
result := 2
if iszero(sload(add(rootSlot, result))) { break }
result := 3
break
}
}
}
/// @dev Returns the number of elements in the set.
function length(Uint256Set storage set) internal view returns (uint256 result) {
result = length(_toBytes32Set(set));
}
/// @dev Returns the number of elements in the set.
function length(Int256Set storage set) internal view returns (uint256 result) {
result = length(_toBytes32Set(set));
}
/// @dev Returns the number of elements in the set.
function length(Uint8Set storage set) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
for { let packed := sload(set.slot) } packed { result := add(1, result) } {
packed := xor(packed, and(packed, add(1, not(packed))))
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(AddressSet storage set, address value) internal view returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for {} 1 {} {
if iszero(shr(160, shl(160, rootPacked))) {
result := 1
if eq(shr(96, rootPacked), value) { break }
if eq(shr(96, sload(add(rootSlot, 1))), value) { break }
if eq(shr(96, sload(add(rootSlot, 2))), value) { break }
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
result := iszero(iszero(sload(keccak256(0x00, 0x40))))
break
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for {} 1 {} {
if iszero(sload(not(rootSlot))) {
result := 1
if eq(sload(rootSlot), value) { break }
if eq(sload(add(rootSlot, 1)), value) { break }
if eq(sload(add(rootSlot, 2)), value) { break }
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
result := iszero(iszero(sload(keccak256(0x00, 0x40))))
break
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(Uint256Set storage set, uint256 value) internal view returns (bool result) {
result = contains(_toBytes32Set(set), bytes32(value));
}
/// @dev Returns whether `value` is in the set.
function contains(Int256Set storage set, int256 value) internal view returns (bool result) {
result = contains(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Returns whether `value` is in the set.
function contains(Uint8Set storage set, uint8 value) internal view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(1, shr(and(0xff, value), sload(set.slot)))
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(AddressSet storage set, address value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
mstore(0x20, rootSlot)
if iszero(n) {
let v0 := shr(96, rootPacked)
if iszero(v0) {
sstore(rootSlot, shl(96, value))
result := 1
break
}
if eq(v0, value) { break }
let v1 := shr(96, sload(add(rootSlot, 1)))
if iszero(v1) {
sstore(add(rootSlot, 1), shl(96, value))
result := 1
break
}
if eq(v1, value) { break }
let v2 := shr(96, sload(add(rootSlot, 2)))
if iszero(v2) {
sstore(add(rootSlot, 2), shl(96, value))
result := 1
break
}
if eq(v2, value) { break }
mstore(0x00, v0)
sstore(keccak256(0x00, 0x40), 1)
mstore(0x00, v1)
sstore(keccak256(0x00, 0x40), 2)
mstore(0x00, v2)
sstore(keccak256(0x00, 0x40), 3)
rootPacked := or(rootPacked, 7)
n := 7
}
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
if iszero(sload(p)) {
n := shr(1, n)
result := 1
sstore(p, add(1, n))
if iszero(n) {
sstore(rootSlot, or(3, shl(96, value)))
break
}
sstore(add(rootSlot, n), shl(96, value))
sstore(rootSlot, add(2, rootPacked))
break
}
break
}
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for { let n := sload(not(rootSlot)) } 1 {} {
mstore(0x20, rootSlot)
if iszero(n) {
let v0 := sload(rootSlot)
if iszero(v0) {
sstore(rootSlot, value)
result := 1
break
}
if eq(v0, value) { break }
let v1 := sload(add(rootSlot, 1))
if iszero(v1) {
sstore(add(rootSlot, 1), value)
result := 1
break
}
if eq(v1, value) { break }
let v2 := sload(add(rootSlot, 2))
if iszero(v2) {
sstore(add(rootSlot, 2), value)
result := 1
break
}
if eq(v2, value) { break }
mstore(0x00, v0)
sstore(keccak256(0x00, 0x40), 1)
mstore(0x00, v1)
sstore(keccak256(0x00, 0x40), 2)
mstore(0x00, v2)
sstore(keccak256(0x00, 0x40), 3)
n := 7
}
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
if iszero(sload(p)) {
n := shr(1, n)
sstore(add(rootSlot, n), value)
sstore(p, add(1, n))
sstore(not(rootSlot), or(1, shl(1, add(1, n))))
result := 1
break
}
break
}
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Uint256Set storage set, uint256 value) internal returns (bool result) {
result = add(_toBytes32Set(set), bytes32(value));
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Int256Set storage set, int256 value) internal returns (bool result) {
result = add(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Uint8Set storage set, uint8 value) internal returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(set.slot)
let mask := shl(and(0xff, value), 1)
sstore(set.slot, or(result, mask))
result := iszero(and(result, mask))
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(AddressSet storage set, address value, uint256 cap)
internal
returns (bool result)
{
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Bytes32Set storage set, bytes32 value, uint256 cap)
internal
returns (bool result)
{
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Uint256Set storage set, uint256 value, uint256 cap)
internal
returns (bool result)
{
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Int256Set storage set, int256 value, uint256 cap) internal returns (bool result) {
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
/// Reverts if the set grows bigger than the custom on-the-fly capacity `cap`.
function add(Uint8Set storage set, uint8 value, uint256 cap) internal returns (bool result) {
if (result = add(set, value)) if (length(set) > cap) revert ExceedsCapacity();
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(AddressSet storage set, address value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
if iszero(n) {
result := 1
if eq(shr(96, rootPacked), value) {
sstore(rootSlot, sload(add(rootSlot, 1)))
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(shr(96, sload(add(rootSlot, 1))), value) {
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(shr(96, sload(add(rootSlot, 2))), value) {
sstore(add(rootSlot, 2), 0)
break
}
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
let position := sload(p)
if iszero(position) { break }
n := sub(shr(1, n), 1)
if iszero(eq(sub(position, 1), n)) {
let lastValue := shr(96, sload(add(rootSlot, n)))
sstore(add(rootSlot, sub(position, 1)), shl(96, lastValue))
mstore(0x00, lastValue)
sstore(keccak256(0x00, 0x40), position)
}
sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), or(shl(1, n), 1)))
sstore(p, 0)
result := 1
break
}
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for { let n := sload(not(rootSlot)) } 1 {} {
if iszero(n) {
result := 1
if eq(sload(rootSlot), value) {
sstore(rootSlot, sload(add(rootSlot, 1)))
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(sload(add(rootSlot, 1)), value) {
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(sload(add(rootSlot, 2)), value) {
sstore(add(rootSlot, 2), 0)
break
}
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
let position := sload(p)
if iszero(position) { break }
n := sub(shr(1, n), 1)
if iszero(eq(sub(position, 1), n)) {
let lastValue := sload(add(rootSlot, n))
sstore(add(rootSlot, sub(position, 1)), lastValue)
mstore(0x00, lastValue)
sstore(keccak256(0x00, 0x40), position)
}
sstore(not(rootSlot), or(shl(1, n), 1))
sstore(p, 0)
result := 1
break
}
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Uint256Set storage set, uint256 value) internal returns (bool result) {
result = remove(_toBytes32Set(set), bytes32(value));
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Int256Set storage set, int256 value) internal returns (bool result) {
result = remove(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Uint8Set storage set, uint8 value) internal returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(set.slot)
let mask := shl(and(0xff, value), 1)
sstore(set.slot, and(result, not(mask)))
result := iszero(iszero(and(result, mask)))
}
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(AddressSet storage set, address value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Bytes32Set storage set, bytes32 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Uint256Set storage set, uint256 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Int256Set storage set, int256 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Shorthand for `isAdd ? set.add(value, cap) : set.remove(value)`.
function update(Uint8Set storage set, uint8 value, bool isAdd, uint256 cap)
internal
returns (bool)
{
return isAdd ? add(set, value, cap) : remove(set, value);
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(AddressSet storage set) internal view returns (address[] memory result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let zs := _ZERO_SENTINEL
let rootPacked := sload(rootSlot)
let n := shr(160, shl(160, rootPacked))
result := mload(0x40)
let o := add(0x20, result)
let v := shr(96, rootPacked)
mstore(o, mul(v, iszero(eq(v, zs))))
for {} 1 {} {
if iszero(n) {
if v {
n := 1
v := shr(96, sload(add(rootSlot, n)))
if v {
n := 2
mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
v := shr(96, sload(add(rootSlot, n)))
if v {
n := 3
mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
}
}
}
break
}
n := shr(1, n)
for { let i := 1 } lt(i, n) { i := add(i, 1) } {
v := shr(96, sload(add(rootSlot, i)))
mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
}
break
}
mstore(result, n)
mstore(0x40, add(o, shl(5, n)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Bytes32Set storage set) internal view returns (bytes32[] memory result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let zs := _ZERO_SENTINEL
let n := sload(not(rootSlot))
result := mload(0x40)
let o := add(0x20, result)
for {} 1 {} {
if iszero(n) {
let v := sload(rootSlot)
if v {
n := 1
mstore(o, mul(v, iszero(eq(v, zs))))
v := sload(add(rootSlot, n))
if v {
n := 2
mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
v := sload(add(rootSlot, n))
if v {
n := 3
mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
}
}
}
break
}
n := shr(1, n)
for { let i := 0 } lt(i, n) { i := add(i, 1) } {
let v := sload(add(rootSlot, i))
mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
}
break
}
mstore(result, n)
mstore(0x40, add(o, shl(5, n)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Uint256Set storage set) internal view returns (uint256[] memory result) {
result = _toUints(values(_toBytes32Set(set)));
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Int256Set storage set) internal view returns (int256[] memory result) {
result = _toInts(values(_toBytes32Set(set)));
}
/// @dev Returns all of the values in the set.
function values(Uint8Set storage set) internal view returns (uint8[] memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let ptr := add(result, 0x20)
let o := 0
for { let packed := sload(set.slot) } packed {} {
if iszero(and(packed, 0xffff)) {
o := add(o, 16)
packed := shr(16, packed)
continue
}
mstore(ptr, o)
ptr := add(ptr, shl(5, and(packed, 1)))
o := add(o, 1)
packed := shr(1, packed)
}
mstore(result, shr(5, sub(ptr, add(result, 0x20))))
mstore(0x40, ptr)
}
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(AddressSet storage set, uint256 i) internal view returns (address result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
result := shr(96, sload(add(rootSlot, i)))
result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
}
if (i >= length(set)) revert IndexOutOfBounds();
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Bytes32Set storage set, uint256 i) internal view returns (bytes32 result) {
result = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
result := sload(add(result, i))
result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
}
if (i >= length(set)) revert IndexOutOfBounds();
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Uint256Set storage set, uint256 i) internal view returns (uint256 result) {
result = uint256(at(_toBytes32Set(set), i));
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Int256Set storage set, uint256 i) internal view returns (int256 result) {
result = int256(uint256(at(_toBytes32Set(set), i)));
}
/// @dev Returns the element at index `i` in the set. Reverts if `i` is out-of-bounds.
function at(Uint8Set storage set, uint256 i) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
let packed := sload(set.slot)
for {} 1 {
mstore(0x00, 0x4e23d035) // `IndexOutOfBounds()`.
revert(0x1c, 0x04)
} {
if iszero(lt(i, 256)) { continue }
for { let j := 0 } iszero(eq(i, j)) {} {
packed := xor(packed, and(packed, add(1, not(packed))))
j := add(j, 1)
}
if iszero(packed) { continue }
break
}
// Find first set subroutine, optimized for smaller bytecode size.
let x := and(packed, add(1, not(packed)))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
result := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the root slot.
function _rootSlot(AddressSet storage s) private pure returns (bytes32 r) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
mstore(0x00, s.slot)
r := keccak256(0x00, 0x24)
}
}
/// @dev Returns the root slot.
function _rootSlot(Bytes32Set storage s) private pure returns (bytes32 r) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
mstore(0x00, s.slot)
r := keccak256(0x00, 0x24)
}
}
/// @dev Casts to a Bytes32Set.
function _toBytes32Set(Uint256Set storage s) private pure returns (Bytes32Set storage c) {
/// @solidity memory-safe-assembly
assembly {
c.slot := s.slot
}
}
/// @dev Casts to a Bytes32Set.
function _toBytes32Set(Int256Set storage s) private pure returns (Bytes32Set storage c) {
/// @solidity memory-safe-assembly
assembly {
c.slot := s.slot
}
}
/// @dev Casts to a uint256 array.
function _toUints(bytes32[] memory a) private pure returns (uint256[] memory c) {
/// @solidity memory-safe-assembly
assembly {
c := a
}
}
/// @dev Casts to a int256 array.
function _toInts(bytes32[] memory a) private pure returns (int256[] memory c) {
/// @solidity memory-safe-assembly
assembly {
c := a
}
}
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an overflow.
error RPowOverflow();
/// @dev The mantissa is too big to fit.
error MantissaOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, due to an multiplication overflow.
error SMulWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error SDivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The input outside the acceptable domain.
error OutOfDomain();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if gt(x, div(not(0), y)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if iszero(eq(div(z, y), x)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, WAD)
// Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
if iszero(mul(y, eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
// This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
if (x <= -41446531673892822313) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
// an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
if iszero(slt(x, 135305999368893231589)) {
mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
revert(0x1c, 0x04)
}
}
// `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// `k` is in the range `[-61, 195]`.
// Evaluate using a (6, 7)-term rational approximation.
// `p` is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already `2**96` too large.
r := sdiv(p, q)
}
// r should be in the range `(0.09, 0.25) * 2**96`.
// We now need to multiply r by:
// - The scale factor `s ≈ 6.031367120`.
// - The `2**k` factor from the range reduction.
// - The `1e18 / 2**96` factor for base conversion.
// We do this all at once, with an intermediate result in `2**213`
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
// We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
// We do this by multiplying by `2**96 / 10**18`. But since
// `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
// and add `ln(2**96 / 10**18)` at the end.
// Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// We place the check here for more optimal stack operations.
if iszero(sgt(x, 0)) {
mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
revert(0x1c, 0x04)
}
// forgefmt: disable-next-item
r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x := shr(159, shl(r, x))
// Evaluate using a (8, 8)-term rational approximation.
// `p` is made monic, we will multiply by a scale factor later.
// forgefmt: disable-next-item
let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
sar(96, mul(add(43456485725739037958740375743393,
sar(96, mul(add(24828157081833163892658089445524,
sar(96, mul(add(3273285459638523848632254066296,
x), x))), x))), x)), 11111509109440967052023855526967)
p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
// `q` is monic by convention.
let q := add(5573035233440673466300451813936, x)
q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
q := add(909429971244387300277376558375, sar(96, mul(x, q)))
// `p / q` is in the range `(0, 0.125) * 2**96`.
// Finalization, we need to:
// - Multiply by the scale factor `s = 5.549…`.
// - Add `ln(2**96 / 10**18)`.
// - Add `k * ln(2)`.
// - Multiply by `10**18 / 2**96 = 5**18 >> 78`.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already `2**96` too large.
p := sdiv(p, q)
// Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
p := mul(1677202110996718588342820967067443963516166, p)
// Add `ln(2) * k * 5**18 * 2**192`.
// forgefmt: disable-next-item
p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
// Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
// Base conversion: mul `2**18 / 2**192`.
r := sar(174, p)
}
}
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
(int256 wad, int256 p) = (int256(WAD), x);
uint256 c; // Whether we need to avoid catastrophic cancellation.
uint256 i = 4; // Number of iterations.
if (w <= 0x1ffffffffffff) {
if (-0x4000000000000 <= w) {
i = 1; // Inputs near zero only take one step to converge.
} else if (w <= -0x3ffffffffffffff) {
i = 32; // Inputs near `-1/e` take very long to converge.
}
} else if (uint256(w >> 63) == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
// Inline log2 for more performance, since the range is small.
let v := shr(49, w)
let l := shl(3, lt(0xff, v))
l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
c := gt(l, 60)
i := add(2, add(gt(l, 53), c))
}
} else {
int256 ll = lnWad(w = lnWad(w));
/// @solidity memory-safe-assembly
assembly {
// `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
i := add(3, iszero(shr(68, x)))
c := iszero(shr(143, x))
}
if (c == uint256(0)) {
do { // If `x` is big, use Newton's so that intermediate values won't overflow.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := mul(w, div(e, wad))
w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
}
if (p <= w) break;
p = w;
} while (--i != uint256(0));
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
return w;
}
}
do { // Otherwise, use Halley's for faster convergence.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := add(w, wad)
let s := sub(mul(w, e), mul(x, wad))
w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
}
if (p <= w) break;
p = w;
} while (--i != c);
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
// For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
// R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
if (c == uint256(0)) return w;
int256 t = w | 1;
/// @solidity memory-safe-assembly
assembly {
x := sdiv(mul(x, wad), t)
}
x = (t * (wad + lnWad(x)));
/// @solidity memory-safe-assembly
assembly {
w := sdiv(x, add(wad, t))
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `a * b == x * y`, with full precision.
function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// 512-bit multiply `[p1 p0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = p1 * 2**256 + p0`.
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
let r := mulmod(x, y, d) // Compute remainder using mulmod.
let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
// Make sure `z` is less than `2**256`. Also prevents `d == 0`.
// Placing the check here seems to give more optimal stack operations.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
d := div(d, t) // Divide `d` by `t`, which is a power of two.
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(2, mul(3, d))
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
z :=
mul(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
z := div(z, d)
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
/// Performs the full 512 bit calculation regardless.
function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z)))
let t := and(d, sub(0, d))
let r := mulmod(x, y, d)
d := div(d, t)
let inv := xor(2, mul(3, d))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
z :=
mul(
or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
mul(sub(2, mul(d, inv)), inv)
)
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
z = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
z := add(z, 1)
if iszero(z) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
/// Throws if result overflows a uint256.
/// Credit to Philogy under MIT license:
/// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Temporarily use `z` as `p0` to save gas.
z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
for {} 1 {} {
if iszero(or(iszero(x), eq(div(z, x), y))) {
let k := and(n, 0xff) // `n`, cleaned.
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
// | p1 | z |
// Before: | p1_0 ¦ p1_1 | z_0 ¦ z_1 |
// Final: | 0 ¦ p1_0 | p1_1 ¦ z_0 |
// Check that final `z` doesn't overflow by checking that p1_0 = 0.
if iszero(shr(k, p1)) {
z := add(shl(sub(256, k), p1), shr(k, z))
break
}
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
z := shr(and(n, 0xff), z)
break
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := div(z, d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(z, d))), div(z, d))
}
}
/// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
/// @solidity memory-safe-assembly
assembly {
let g := n
let r := mod(a, n)
for { let y := 1 } 1 {} {
let q := div(g, r)
let t := g
g := r
r := sub(t, mul(r, q))
let u := x
x := y
y := sub(u, mul(y, q))
if iszero(r) { break }
}
x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
mstore(0x00, 0x65244e4e) // `DivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `max(0, x - y)`.
function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x + y)`.
function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(0, lt(add(x, y), x)), add(x, y))
}
}
/// @dev Returns `min(2 ** 256 - 1, x * y)`.
function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Returns `x != 0 ? x : y`, without branching.
function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != bytes32(0) ? x : y`, without branching.
function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(x)))
}
}
/// @dev Returns `x != address(0) ? x : y`, without branching.
function coalesce(address x, address y) internal pure returns (address z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, mul(y, iszero(shl(96, x))))
}
}
/// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
/// Reverts if the computation overflows.
function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
if x {
z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
let half := shr(1, b) // Divide `b` by 2.
// Divide `y` by 2 every iteration.
for { y := shr(1, y) } y { y := shr(1, y) } {
let xx := mul(x, x) // Store x squared.
let xxRound := add(xx, half) // Round to the nearest number.
// Revert if `xx + half` overflowed, or if `x ** 2` overflows.
if or(lt(xxRound, xx), shr(128, x)) {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
x := div(xxRound, b) // Set `x` to scaled `xxRound`.
// If `y` is odd:
if and(y, 1) {
let zx := mul(z, x) // Compute `z * x`.
let zxRound := add(zx, half) // Round to the nearest number.
// If `z * x` overflowed or `zx + half` overflowed:
if or(xor(div(zx, x), z), lt(zxRound, zx)) {
// Revert if `x` is non-zero.
if x {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
}
z := div(zxRound, b) // Return properly scaled `zxRound`.
}
}
}
}
}
/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) internal 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))
}
}
/// @dev Returns the cube root of `x`, rounded down.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// Makeshift lookup table to nudge the approximate log2 result.
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
// Newton-Raphson's.
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
// Round down.
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
z = (1 + sqrt(x)) * 10 ** 9;
z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
z = (1 + cbrt(x)) * 10 ** 12;
z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
}
/// @solidity memory-safe-assembly
assembly {
let p := x
for {} 1 {} {
if iszero(shr(229, p)) {
if iszero(shr(199, p)) {
p := mul(p, 100000000000000000) // 10 ** 17.
break
}
p := mul(p, 100000000) // 10 ** 8.
break
}
if iszero(shr(249, p)) { p := mul(p, 100) }
break
}
let t := mulmod(mul(z, z), z, p)
z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { z := mul(z, x) }
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
/// Returns 0 if `x` is zero.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Returns the log2 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log2Up(uint256 x) internal pure returns (uint256 r) {
r = log2(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(r, 1), x))
}
}
/// @dev Returns the log10 of `x`.
/// Returns 0 if `x` is zero.
function log10(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, 100000000000000000000000000000000000000)) {
x := div(x, 100000000000000000000000000000000000000)
r := 38
}
if iszero(lt(x, 100000000000000000000)) {
x := div(x, 100000000000000000000)
r := add(r, 20)
}
if iszero(lt(x, 10000000000)) {
x := div(x, 10000000000)
r := add(r, 10)
}
if iszero(lt(x, 100000)) {
x := div(x, 100000)
r := add(r, 5)
}
r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
}
}
/// @dev Returns the log10 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log10Up(uint256 x) internal pure returns (uint256 r) {
r = log10(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(exp(10, r), x))
}
}
/// @dev Returns the log256 of `x`.
/// Returns 0 if `x` is zero.
function log256(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(shr(3, r), lt(0xff, shr(r, x)))
}
}
/// @dev Returns the log256 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log256Up(uint256 x) internal pure returns (uint256 r) {
r = log256(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(shl(3, r), 1), x))
}
}
/// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
/// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
/// @solidity memory-safe-assembly
assembly {
mantissa := x
if mantissa {
if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
mantissa := div(mantissa, 1000000000000000000000000000000000)
exponent := 33
}
if iszero(mod(mantissa, 10000000000000000000)) {
mantissa := div(mantissa, 10000000000000000000)
exponent := add(exponent, 19)
}
if iszero(mod(mantissa, 1000000000000)) {
mantissa := div(mantissa, 1000000000000)
exponent := add(exponent, 12)
}
if iszero(mod(mantissa, 1000000)) {
mantissa := div(mantissa, 1000000)
exponent := add(exponent, 6)
}
if iszero(mod(mantissa, 10000)) {
mantissa := div(mantissa, 10000)
exponent := add(exponent, 4)
}
if iszero(mod(mantissa, 100)) {
mantissa := div(mantissa, 100)
exponent := add(exponent, 2)
}
if iszero(mod(mantissa, 10)) {
mantissa := div(mantissa, 10)
exponent := add(exponent, 1)
}
}
}
}
/// @dev Convenience function for packing `x` into a smaller number using `sci`.
/// The `mantissa` will be in bits [7..255] (the upper 249 bits).
/// The `exponent` will be in bits [0..6] (the lower 7 bits).
/// Use `SafeCastLib` to safely ensure that the `packed` number is small
/// enough to fit in the desired unsigned integer type:
/// ```
/// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
/// ```
function packSci(uint256 x) internal pure returns (uint256 packed) {
(x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
/// @solidity memory-safe-assembly
assembly {
if shr(249, x) {
mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
revert(0x1c, 0x04)
}
packed := or(shl(7, x), packed)
}
}
/// @dev Convenience function for unpacking a packed number from `packSci`.
function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
unchecked {
unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards zero.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (x & y & 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
}
}
/// @dev Returns greatest common divisor of `x` and `y`.
function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
for { z := x } y {} {
let t := y
y := mod(z, y)
z := t
}
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
internal
pure
returns (uint256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
unchecked {
if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
return a - fullMulDiv(a - b, t - begin, end - begin);
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
internal
pure
returns (int256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
// forgefmt: disable-next-item
unchecked {
if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
uint256(t - begin), uint256(end - begin)));
return int256(uint256(a) - fullMulDiv(uint256(a - b),
uint256(t - begin), uint256(end - begin)));
}
}
/// @dev Returns if `x` is an even number. Some people may need this.
function isEven(uint256 x) internal pure returns (bool) {
return x & uint256(1) == uint256(0);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RAW NUMBER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(x, y)
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mod(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := smod(x, y)
}
}
/// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := addmod(x, y, d)
}
}
/// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mulmod(x, y, d)
}
}
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.19;
interface IBigcoin {
function mint(address to, uint256 amt) external;
function burn(uint256 value) external;
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);
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @dev Miners that can be purchased in the game.
*/
struct Miner {
uint256 minerIndex;
uint256 id;
uint256 x;
uint256 y;
uint256 hashrate;
uint256 powerConsumption;
uint256 cost;
bool inProduction;
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @dev Current player's facility.
*/
struct Facility {
uint256 facilityIndex;
uint256 maxMiners; // x * y
uint256 currMiners;
uint256 totalPowerOutput;
uint256 currPowerOutput;
uint256 x; // number of x quadrants
uint256 y; // number of y qudrants
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @dev Facilities that can be purchased in game.
*/
struct NewFacility {
uint256 maxMiners;
uint256 totalPowerOutput;
uint256 cost;
bool inProduction;
uint256 x; // number of x quadrants
uint256 y; // number of y qudrants
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
library Errors {
error IncorrectValue();
error AlreadyPurchasedInitialFactory();
error StarterMinerAlreadyAcquired();
error FacilityAtMaxCapacity();
error FacilityInadequatePowerOutput();
error PlayerDoesNotOwnMiner();
error GreatDepression();
error MinerNotInProduction();
error TooPoor();
error NewFacilityNotInProduction();
error CannotDowngradeAFacility();
error NoRewardsPending();
error CannotDecreaseBelowZero();
error InvalidMinerCoordinates();
error FacilityDimensionsInvalid();
error NeedToInitializeFacility();
error InvalidReferrer();
error NonExistentMiner();
error CantModifyStarterMiner();
error NonExistentFacility();
error CantModifyStarterFacility();
error AlreadyAtMaxFacility();
error CantBuyNewFacilityYet();
error InvalidMinerIndex();
error InvalidFacilityIndex();
error InvalidFee();
error InvalidPowerOutput();
error MiningHasntStarted();
error WithdrawFailed();
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
library Events {
event MiningStarted(uint256 startBlock);
event NewMinerAdded(
uint256 indexed minerIndex, uint256 hashRate, uint256 powerConsumption, uint256 cost, bool inProduction
);
event MinerProductionToggled(uint256 indexed minerIndex, bool inProduction);
event FacilityProductionToggled(uint256 indexed facilityIndex, bool inProduction);
event NewFacilityAdded(
uint256 indexed facilityIndex, uint256 totalPowerOutput, uint256 cost, bool inProduction, uint256 x, uint256 y
);
event MinerSecondaryMarketAdded(uint256 indexed minerIndex, uint256 price);
event InitialFacilityPurchased(address indexed player);
event FreeMinerRedeemed(address indexed player);
event MinerSold(
address indexed player,
uint256 indexed minerIndex,
uint256 secondHandPrice,
uint256 minerId,
uint256 x,
uint256 y
);
event MinerBought(
address indexed player, uint256 indexed minerIndex, uint256 cost, uint256 minerId, uint256 x, uint256 y
);
event FacilityBought(address indexed player, uint256 indexed facilityIndex, uint256 cost);
event PlayerHashrateIncreased(address indexed player, uint256 playerHashrate, uint256 playerPendingRewards);
event PlayerHashrateDecreased(address indexed player, uint256 playerHashrate, uint256 playerPendingRewards);
event RewardsClaimed(address indexed player, uint256 rewards);
event MinerCostChanged(uint256 indexed minerIndex, uint256 newCost);
event FacilityCostChanged(uint256 indexed facilityIndex, uint256 newCost);
}
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// 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;
}
}