Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 16 from a total of 16 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Set Subscription... | 18030508 | 932 days ago | IN | 0 ETH | 0.00051391 | ||||
| Set Subscription... | 18030493 | 932 days ago | IN | 0 ETH | 0.00049951 | ||||
| Set Subscription... | 18030491 | 932 days ago | IN | 0 ETH | 0.00050115 | ||||
| Set Burn Percent | 18030487 | 932 days ago | IN | 0 ETH | 0.0004663 | ||||
| Burn ETH | 18007859 | 935 days ago | IN | 0 ETH | 0.00168863 | ||||
| Register Address... | 18007799 | 935 days ago | IN | 0.1 ETH | 0.00100932 | ||||
| Set Subscription... | 18001504 | 936 days ago | IN | 0 ETH | 0.00040219 | ||||
| Register Address... | 18001426 | 936 days ago | IN | 0.015 ETH | 0.00088573 | ||||
| Register Address... | 18001417 | 936 days ago | IN | 0.015 ETH | 0.00085814 | ||||
| Burn ETH | 17972896 | 940 days ago | IN | 0 ETH | 0.00638151 | ||||
| Set Burn Percent | 17972892 | 940 days ago | IN | 0 ETH | 0.00129169 | ||||
| Set Subscription... | 17972885 | 940 days ago | IN | 0 ETH | 0.00133886 | ||||
| Register Address... | 17972868 | 940 days ago | IN | 0.06 ETH | 0.00605611 | ||||
| Register Address... | 17972646 | 940 days ago | IN | 0.03 ETH | 0.00217277 | ||||
| Register Address... | 17972643 | 940 days ago | IN | 0.015 ETH | 0.00132923 | ||||
| Register Address... | 17972637 | 940 days ago | IN | 0.015 ETH | 0.00137061 |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SubscriptionManager
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 500000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/**
* Kekotron Subscription Manager
*
* Website: nchart.io
* Docs: docs.nchart.io
* twitter.com/Nchart_
* twitter.com/Kekotron_
*/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "@solady/auth/OwnableRoles.sol";
import "@solady/utils/LibMap.sol";
import {Nchart} from "./Nchart.sol";
import "./IUniswapV2Router02.sol";
/**
* ........
* ..::::::::::::. .
* .:::::::::::::::. =+-.
* --::::::::::::::::. =+++-
* *##*+::::::::::::::. =+++++
* *#####: .::::::::::. =++++++
* -######: .:::::::. =++++++-
* *######: :. .::::. =+++++++
* #######: -=-:. .:. =+++++++
* +######: -=====:. =++++++=
* :######: -========-. =++++++:
* +#####: -===========-.-+++++=
* =####: -==============-==+-
* :*##: -================-.
* :+: -==============-.
* :==========-:.
* ......
*
*
* @dev Contract which accepts ETH to pay for a subscription, which is used to buy and burn CHART from
* @dev the UniswapV2 LP Pool
*/
contract SubscriptionManager is OwnableRoles {
uint40 internal constant MAX_UINT40 = type(uint40).max;
uint256 public constant KEEPER_ROLE = uint256(1);
Nchart public immutable chart;
IUniswapV2Router02 public immutable router;
LibMap.Uint40Map private expiration;
uint256 public subscriptionPrice = 0.015 ether; // Price is 0.015e to start
uint256 public referralFeePriceReduction = 0.001 ether; // Reduction of fee per address if referral is provided
uint40 public subscriptionLength; // Lifetime subscription to start
// Percent of fees to use to buy and burn CHART
uint8 public burnPercent = 99;
// Percent of fees to send to referrer if they have an active subscription
uint8 public referralPercent = 5;
event BurnPercentUpdated(uint8 newPercent, uint8 oldPercent);
event ReferralPaid(address indexed referrer, uint256 amount);
event ReferralFeePriceReductionUpdated(uint256 newAmount, uint256 oldAmount);
event ReferralPercentUpdated(uint8 newPercent, uint8 oldPercent);
event SubscriptionLengthUpdated(uint256 newLength, uint256 oldLength);
event SubscriptionPaid(address indexed subscriber, uint40 expirationTimestamp, uint256 price);
event SubscriptionPriceUpdated(uint256 newPrice, uint256 oldPrice);
error SubscriptionManager__BurnPercentMustBeGreaterThan50();
error SubscriptionManager__BurnPercentMustBeLessThan100();
error SubscriptionManager__CannotReferSelf();
error SubscriptionManager__CannotReduceMoreThanSubscriptionPrice();
error SubscriptionManager__CannotRegisterAddressZero();
error SubscriptionManager__CanOnlyIncreaseExpiration();
error SubscriptionManager__ErrorRetrievingPriceFromDataFeed();
error SubscriptionManager__ErrorSendingKeeperFunds();
error SubscriptionManager__InvalidETHAmountProvided(uint256 msgValue, uint256 ethRequired);
error SubscriptionManager__MaxFiftyPercentReferralPercent();
error SubscriptionManager__MustProvideAtLeastOneAddress();
error SubscriptionManager__OnlyOwner();
error SubscriptionManager__UseRegisterAddressesFunction();
constructor(address payable chart_, address owner_, address router_) {
_initializeOwner(owner_);
chart = Nchart(chart_);
router = IUniswapV2Router02(router_);
}
receive() external payable {
revert SubscriptionManager__UseRegisterAddressesFunction();
}
fallback() external payable {
revert SubscriptionManager__UseRegisterAddressesFunction();
}
/**
* @notice Sets the length of the subscription period.
*
* @dev The subscription length is either added to the remaining time of a user's subscription or block.timestamp if unset
* @dev Passing in a value of 0 will set the subscription to unlimited, and expiration will be set to MAX_UINT40
* @dev - Throws if caller is not owner
*
* @dev On completion:
* @dev - `subscriptionLength` = `newSubscriptionLength`
* @dev - Emits {SubscriptionLengthUpdated} event
*
* @param newSubscriptionLength Length to update future subscriptions to
*/
function setSubscriptionLength(uint40 newSubscriptionLength) external {
_requireIsOwner();
uint40 oldSubscriptionLength = subscriptionLength;
subscriptionLength = newSubscriptionLength;
emit SubscriptionLengthUpdated(newSubscriptionLength, oldSubscriptionLength);
}
/**
* @notice Sets the percentage of fees to send to referrers if they have an active subscription
*
* @dev - Throws if `msg.sender` != `owner()`
* @dev - Throws if provided percent is > 5
*
* @dev On completion:
* @dev - `referralPercent` = `newPercent`
* @dev - Emits {ReferralPercentUpdated} event
*
* @param newPercent Percentage of fees to send to referrers
*/
function setReferralPercent(uint8 newPercent) external {
_requireIsOwner();
if (newPercent > 50) {
revert SubscriptionManager__MaxFiftyPercentReferralPercent();
}
uint8 oldPercent = referralPercent;
referralPercent = newPercent;
emit ReferralPercentUpdated(newPercent, oldPercent);
}
/**
* @notice Sets the amount fees are reduced per account when providing a referral
*
* @dev - Throws if `msg.sender` != `owner()`
* @dev - Throws if provided reduction amount is greater than subscription price
*
* @dev On completion:
* @dev - `referralFeePriceReduction` = `newAmount`
* @dev - Emits {ReferralFeePriceReductionUpdated} event
*
* @param newAmount Amount to reduce fees per address if referral provided
*/
function setReferralFeePriceReduction(uint256 newAmount) external {
_requireIsOwner();
if (newAmount >= subscriptionPrice) {
revert SubscriptionManager__CannotReduceMoreThanSubscriptionPrice();
}
uint256 oldReferralFeePriceReduction = referralFeePriceReduction;
referralFeePriceReduction = newAmount;
emit ReferralFeePriceReductionUpdated(newAmount, oldReferralFeePriceReduction);
}
/**
* @notice Sets the price of new subscriptions. Users will be charged this amount per address to use premium features
*
* @dev Price of subscription is set in ETH, stored in 1e18 (wei)
* @dev Passing in a value of 0 will set premium features to free.
* @dev - Throws if caller is not owner
*
* @dev On completion:
* @dev - `subscriptionPrice` = `newSubscriptionPrice`
* @dev - Emits {SubscriptionPriceUpdated} event
*
* @param newSubscriptionPrice Price to update future subscriptions to
*/
function setSubscriptionPrice(uint256 newSubscriptionPrice) external {
_requireIsOwner();
uint256 oldSubscriptionPrice = subscriptionPrice;
subscriptionPrice = newSubscriptionPrice;
emit SubscriptionPriceUpdated(newSubscriptionPrice, oldSubscriptionPrice);
}
/**
* @notice Sets the percentage of fees used to buy and burn CHART
*
* @dev - Throws if `msg.sender` != `owner()`
* @dev - Throws if provided percent is > 100
*
* @dev On completion:
* @dev - `burnPercent` = `newPercent`
* @dev - Emits {BurnPercentUpdated} event
*
* @param newPercent Percentage of fees to be used to buy and burn CHART
*/
function setBurnPercent(uint8 newPercent) external {
_requireIsOwner();
if (newPercent > 100) {
revert SubscriptionManager__BurnPercentMustBeLessThan100();
}
if (newPercent < 50) {
revert SubscriptionManager__BurnPercentMustBeGreaterThan50();
}
uint8 oldPercent = burnPercent;
burnPercent = newPercent;
emit BurnPercentUpdated(newPercent, oldPercent);
}
/**
* @notice Allows owner to increase the expiration timestamp for a provided user in case of
* @notice giveaways.
*
* @dev - Throws if `newExpiration` < current expiration
* @dev - Throws if `msg.sender` != `owner()
*
* @dev On completion:
* @dev - User expiration is set to `newExpiration`
*/
function setExpirationTimestamp(uint40 newExpiration, address user) external {
_requireIsOwner();
if (newExpiration < LibMap.get(expiration, uint256(uint160(user)))){
revert SubscriptionManager__CanOnlyIncreaseExpiration();
}
LibMap.set(expiration, uint256(uint160(user)), newExpiration);
}
/**
* @notice Grants the KEEPER_ROLE to the provided user.
*
* @dev - Throws if the `msg.sender` is not `owner()`
*
* @dev On completion:
* @dev - `newKeeper` is assigned the `KEEPER_ROLE`
*
* @param newKeeper Address to assign the `KEEPER_ROLE`
*/
function grantKeeperRole(address newKeeper) external {
grantRoles(newKeeper, KEEPER_ROLE);
}
/**
* @notice Revokes the KEEPER_ROLE from the provided user.
*
* @dev - Throws if the `msg.sender` is not `owner()`
*
* @dev On completion:
* @dev - `toRevoke` is no longer assigned the `KEEPER_ROLE`
*
* @param toRevoke Address to revoke the `KEEPER_ROLE` from
*/
function revokeKeeperRole(address toRevoke) external {
revokeRoles(toRevoke, KEEPER_ROLE);
}
/**
* @notice Registers a list of addresses for premium features. There is an optional referrer address
* @notice which will receive a percentage of the fees paid by the registered addresses if they have an active subscription.
*
* @dev - Throws if the length of the provided addresses is 0
* @dev - Throws if `msg.value` is not equal to the exact amount required to pay for the subscriptions
* @dev - Throws if any address provided is address(0)
* @dev - Throws if the referrer address executes code > 2300 gas
* @dev - If the provided referral address is not subscribed, it is a no-op
*
* @dev On completion:
* @dev - `expiration` mapping for each address is updated to add on an additional `subLength` seconds
* @dev - If `expiration` + the current length of subscriptions is > uint256 max, set to uint256 max
* @dev - If the current length of subscription is 0, set `expiration` to uint256 max
* @dev - The contract has `subLength` * `subPrice` - `referralAmount` more ETH
* @dev - If the referrer is subscribed, they receive `referralAmount` ETH
* @dev - Emits `addresses.length` {SubscriptionPaid} events
* @dev - Emits {ReferralPaid} event if referrer is subscribed
*
* @param addresses A list of addresses to register
* @param referrer Optional address of referrer
*/
function registerAddresses(address[] calldata addresses, address referrer) external payable {
uint256 numSubs = addresses.length;
if (numSubs == 0) {
revert SubscriptionManager__MustProvideAtLeastOneAddress();
}
if (referrer == msg.sender) {
revert SubscriptionManager__CannotReferSelf();
}
uint256 subPrice = subscriptionPrice;
uint256 referralFeeReduction = referrer != address(0) ? referralFeePriceReduction * numSubs : 0;
uint256 ethRequired = numSubs * subPrice;
uint256 referralAmount;
if (block.timestamp <= LibMap.get(expiration, uint256(uint160(referrer)))) {
if (referralPercent > 0) {
referralAmount = ethRequired * referralPercent / 100;
}
}
if (referralFeeReduction > 0) {
ethRequired -= referralFeeReduction;
}
if (msg.value != ethRequired) {
revert SubscriptionManager__InvalidETHAmountProvided(msg.value, ethRequired);
}
uint40 subLength = subscriptionLength;
if (subLength == 0) {
for (uint256 i = 0; i < numSubs;) {
address addr = addresses[i];
_requireValidAddress(addr);
LibMap.set(expiration, uint256(uint160(addr)), type(uint40).max);
emit SubscriptionPaid(addr, MAX_UINT40, subPrice);
unchecked {
++i;
}
}
} else {
uint40 maxExpiration = type(uint40).max - subLength;
for (uint256 i = 0; i < numSubs;) {
address addr = addresses[i];
_requireValidAddress(addr);
uint40 addrExpiration = LibMap.get(expiration, uint256(uint160(addr)));
uint40 timestamp = uint40(block.timestamp);
if (addrExpiration < timestamp) {
if (timestamp > maxExpiration) {
addrExpiration = type(uint40).max;
} else {
addrExpiration = timestamp + subLength;
}
} else if (addrExpiration < maxExpiration) {
// Unchecked is safe here as we know that expiration + subLength < MAX_UINT40
unchecked {
addrExpiration += subLength;
}
} else {
addrExpiration = type(uint40).max;
}
LibMap.set(expiration, uint256(uint160(addr)), addrExpiration);
emit SubscriptionPaid(addr, addrExpiration, subPrice);
unchecked {
++i;
}
}
}
if (referralAmount > 0) {
// We use `transfer` here to limit the amount of gas forwarded to the referrer
// As such, referrer addresses should be EOAs or contracts without fallback / receive functionality
payable(referrer).transfer(referralAmount);
emit ReferralPaid(referrer, referralAmount);
}
}
/**
* @notice Uses fees from subscriptions to buyback and burn CHART from the uniswap v2 pair
*
* @dev This is a high risk function, anyone with KEEPER_ROLE could potentially sandwich this call
* @dev Do not give KEEPER_ROLE to addresses unless you fully trust them
* @dev If you would like to ignore slippage, pass in 0 for amountOutMin
* @dev - Throws if `msg.sender` does not have owner or `KEEPER_ROLE`
* @dev - Throws if there is an error sending funds to `msg.sender`
*
* @dev On completion:
* @dev - `burnPercent`% of the balance of the contract is used to buy and burn CHART
* @dev - The remaining balance is sent to the `msg.sender` to cover operational expenses
* @dev - address(this).balance == 0
*
* @param amountOutMin Minimum amount of CHART to receive from `burnPercent`% * balance ETH
*/
function burnETH(uint256 amountOutMin) external {
_checkRolesOrOwner(KEEPER_ROLE);
uint256 balance = address(this).balance;
uint256 amountToBurn = balance * burnPercent / 100;
uint256 amountToSend = balance - amountToBurn;
address[] memory path = new address[](2);
path[0] = router.WETH();
path[1] = address(chart);
router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: amountToBurn}(
amountOutMin,
path,
address(0xdead),
block.timestamp
);
// Gated role, do not need to worry about gas to forward
(bool success,) = payable(msg.sender).call{value: amountToSend}("");
if (!success) {
revert SubscriptionManager__ErrorSendingKeeperFunds();
}
}
/**
* @notice Returns if an address has a current subscription
*
* @param user Address of user
* @return True if user has an active subscription, false if not
*/
function isAddressRegistered(address user) external view returns (bool) {
return block.timestamp <= LibMap.get(expiration, uint256(uint160(user)));
}
function getExpiration(address user) external view returns (uint40) {
return LibMap.get(expiration, uint256(uint160(user)));
}
/// @dev Convenience function to require user is owner
function _requireIsOwner() internal view {
if (msg.sender != owner()) {
revert SubscriptionManager__OnlyOwner();
}
}
/// @dev Convenience function to validate address input
function _requireValidAddress(address addr) internal pure {
if (addr == address(0)) {
revert SubscriptionManager__CannotRegisterAddressZero();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Ownable} from "./Ownable.sol";
/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover and roles
/// may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `user`'s roles is updated to `roles`.
/// Each bit of `roles` represents whether the role is set.
event RolesUpdated(address indexed user, uint256 indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The role slot of `user` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
/// let roleSlot := keccak256(0x00, 0x20)
/// ```
/// This automatically ignores the upper bits of the `user` in case
/// they are not clean, as well as keep the `keccak256` under 32-bytes.
///
/// Note: This is equal to `_OWNER_SLOT_NOT` in for gas efficiency.
uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Overwrite the roles directly without authorization guard.
function _setRoles(address user, uint256 roles) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Store the new value.
sstore(keccak256(0x0c, 0x20), roles)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Updates the roles directly without authorization guard.
/// If `on` is true, each set bit of `roles` will be turned on,
/// otherwise, each set bit of `roles` will be turned off.
function _updateRoles(address user, uint256 roles, bool on) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
// Load the current value.
let current := sload(roleSlot)
// Compute the updated roles if `on` is true.
let updated := or(current, roles)
// Compute the updated roles if `on` is false.
// Use `and` to compute the intersection of `current` and `roles`,
// `xor` it with `current` to flip the bits in the intersection.
if iszero(on) { updated := xor(current, and(current, roles)) }
// Then, store the new value.
sstore(roleSlot, updated)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
}
}
/// @dev Grants the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn on.
function _grantRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, true);
}
/// @dev Removes the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn off.
function _removeRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, false);
}
/// @dev Throws if the sender does not have any of the `roles`.
function _checkRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Throws if the sender is not the owner,
/// and does not have any of the `roles`.
/// Checks for ownership first, then lazily checks for roles.
function _checkOwnerOrRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Throws if the sender does not have any of the `roles`,
/// and is not the owner.
/// Checks for roles first, then lazily checks for ownership.
function _checkRolesOrOwner(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
// We don't need to mask the values of `ordinals`, as Solidity
// cleans dirty upper bits when storing variables into memory.
roles := or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
/// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
/// @solidity memory-safe-assembly
assembly {
// Grab the pointer to the free memory.
ordinals := mload(0x40)
let ptr := add(ordinals, 0x20)
let o := 0
// The absence of lookup tables, De Bruijn, etc., here is intentional for
// smaller bytecode, as this function is not meant to be called on-chain.
for { let t := roles } 1 {} {
mstore(ptr, o)
// `shr` 5 is equivalent to multiplying by 0x20.
// Push back into the ordinals array if the bit is set.
ptr := add(ptr, shl(5, and(t, 1)))
o := add(o, 1)
t := shr(o, roles)
if iszero(t) { break }
}
// Store the length of `ordinals`.
mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
// Allocate the memory.
mstore(0x40, ptr)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to grant `user` `roles`.
/// If the `user` already has a role, then it will be an no-op for the role.
function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
_grantRoles(user, roles);
}
/// @dev Allows the owner to remove `user` `roles`.
/// If the `user` does not have a role, then it will be an no-op for the role.
function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
_removeRoles(user, roles);
}
/// @dev Allow the caller to remove their own roles.
/// If the caller does not have a role, then it will be an no-op for the role.
function renounceRoles(uint256 roles) public payable virtual {
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the roles of `user`.
function rolesOf(address user) public view virtual returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value.
roles := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns whether `user` has any of `roles`.
function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles != 0;
}
/// @dev Returns whether `user` has all of `roles`.
function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles == roles;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by an account with `roles`.
modifier onlyRoles(uint256 roles) virtual {
_checkRoles(roles);
_;
}
/// @dev Marks a function as only callable by the owner or by an account
/// with `roles`. Checks for ownership first, then lazily checks for roles.
modifier onlyOwnerOrRoles(uint256 roles) virtual {
_checkOwnerOrRoles(roles);
_;
}
/// @dev Marks a function as only callable by an account with `roles`
/// or the owner. Checks for roles first, then lazily checks for ownership.
modifier onlyRolesOrOwner(uint256 roles) virtual {
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ROLE CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// IYKYK
uint256 internal constant _ROLE_0 = 1 << 0;
uint256 internal constant _ROLE_1 = 1 << 1;
uint256 internal constant _ROLE_2 = 1 << 2;
uint256 internal constant _ROLE_3 = 1 << 3;
uint256 internal constant _ROLE_4 = 1 << 4;
uint256 internal constant _ROLE_5 = 1 << 5;
uint256 internal constant _ROLE_6 = 1 << 6;
uint256 internal constant _ROLE_7 = 1 << 7;
uint256 internal constant _ROLE_8 = 1 << 8;
uint256 internal constant _ROLE_9 = 1 << 9;
uint256 internal constant _ROLE_10 = 1 << 10;
uint256 internal constant _ROLE_11 = 1 << 11;
uint256 internal constant _ROLE_12 = 1 << 12;
uint256 internal constant _ROLE_13 = 1 << 13;
uint256 internal constant _ROLE_14 = 1 << 14;
uint256 internal constant _ROLE_15 = 1 << 15;
uint256 internal constant _ROLE_16 = 1 << 16;
uint256 internal constant _ROLE_17 = 1 << 17;
uint256 internal constant _ROLE_18 = 1 << 18;
uint256 internal constant _ROLE_19 = 1 << 19;
uint256 internal constant _ROLE_20 = 1 << 20;
uint256 internal constant _ROLE_21 = 1 << 21;
uint256 internal constant _ROLE_22 = 1 << 22;
uint256 internal constant _ROLE_23 = 1 << 23;
uint256 internal constant _ROLE_24 = 1 << 24;
uint256 internal constant _ROLE_25 = 1 << 25;
uint256 internal constant _ROLE_26 = 1 << 26;
uint256 internal constant _ROLE_27 = 1 << 27;
uint256 internal constant _ROLE_28 = 1 << 28;
uint256 internal constant _ROLE_29 = 1 << 29;
uint256 internal constant _ROLE_30 = 1 << 30;
uint256 internal constant _ROLE_31 = 1 << 31;
uint256 internal constant _ROLE_32 = 1 << 32;
uint256 internal constant _ROLE_33 = 1 << 33;
uint256 internal constant _ROLE_34 = 1 << 34;
uint256 internal constant _ROLE_35 = 1 << 35;
uint256 internal constant _ROLE_36 = 1 << 36;
uint256 internal constant _ROLE_37 = 1 << 37;
uint256 internal constant _ROLE_38 = 1 << 38;
uint256 internal constant _ROLE_39 = 1 << 39;
uint256 internal constant _ROLE_40 = 1 << 40;
uint256 internal constant _ROLE_41 = 1 << 41;
uint256 internal constant _ROLE_42 = 1 << 42;
uint256 internal constant _ROLE_43 = 1 << 43;
uint256 internal constant _ROLE_44 = 1 << 44;
uint256 internal constant _ROLE_45 = 1 << 45;
uint256 internal constant _ROLE_46 = 1 << 46;
uint256 internal constant _ROLE_47 = 1 << 47;
uint256 internal constant _ROLE_48 = 1 << 48;
uint256 internal constant _ROLE_49 = 1 << 49;
uint256 internal constant _ROLE_50 = 1 << 50;
uint256 internal constant _ROLE_51 = 1 << 51;
uint256 internal constant _ROLE_52 = 1 << 52;
uint256 internal constant _ROLE_53 = 1 << 53;
uint256 internal constant _ROLE_54 = 1 << 54;
uint256 internal constant _ROLE_55 = 1 << 55;
uint256 internal constant _ROLE_56 = 1 << 56;
uint256 internal constant _ROLE_57 = 1 << 57;
uint256 internal constant _ROLE_58 = 1 << 58;
uint256 internal constant _ROLE_59 = 1 << 59;
uint256 internal constant _ROLE_60 = 1 << 60;
uint256 internal constant _ROLE_61 = 1 << 61;
uint256 internal constant _ROLE_62 = 1 << 62;
uint256 internal constant _ROLE_63 = 1 << 63;
uint256 internal constant _ROLE_64 = 1 << 64;
uint256 internal constant _ROLE_65 = 1 << 65;
uint256 internal constant _ROLE_66 = 1 << 66;
uint256 internal constant _ROLE_67 = 1 << 67;
uint256 internal constant _ROLE_68 = 1 << 68;
uint256 internal constant _ROLE_69 = 1 << 69;
uint256 internal constant _ROLE_70 = 1 << 70;
uint256 internal constant _ROLE_71 = 1 << 71;
uint256 internal constant _ROLE_72 = 1 << 72;
uint256 internal constant _ROLE_73 = 1 << 73;
uint256 internal constant _ROLE_74 = 1 << 74;
uint256 internal constant _ROLE_75 = 1 << 75;
uint256 internal constant _ROLE_76 = 1 << 76;
uint256 internal constant _ROLE_77 = 1 << 77;
uint256 internal constant _ROLE_78 = 1 << 78;
uint256 internal constant _ROLE_79 = 1 << 79;
uint256 internal constant _ROLE_80 = 1 << 80;
uint256 internal constant _ROLE_81 = 1 << 81;
uint256 internal constant _ROLE_82 = 1 << 82;
uint256 internal constant _ROLE_83 = 1 << 83;
uint256 internal constant _ROLE_84 = 1 << 84;
uint256 internal constant _ROLE_85 = 1 << 85;
uint256 internal constant _ROLE_86 = 1 << 86;
uint256 internal constant _ROLE_87 = 1 << 87;
uint256 internal constant _ROLE_88 = 1 << 88;
uint256 internal constant _ROLE_89 = 1 << 89;
uint256 internal constant _ROLE_90 = 1 << 90;
uint256 internal constant _ROLE_91 = 1 << 91;
uint256 internal constant _ROLE_92 = 1 << 92;
uint256 internal constant _ROLE_93 = 1 << 93;
uint256 internal constant _ROLE_94 = 1 << 94;
uint256 internal constant _ROLE_95 = 1 << 95;
uint256 internal constant _ROLE_96 = 1 << 96;
uint256 internal constant _ROLE_97 = 1 << 97;
uint256 internal constant _ROLE_98 = 1 << 98;
uint256 internal constant _ROLE_99 = 1 << 99;
uint256 internal constant _ROLE_100 = 1 << 100;
uint256 internal constant _ROLE_101 = 1 << 101;
uint256 internal constant _ROLE_102 = 1 << 102;
uint256 internal constant _ROLE_103 = 1 << 103;
uint256 internal constant _ROLE_104 = 1 << 104;
uint256 internal constant _ROLE_105 = 1 << 105;
uint256 internal constant _ROLE_106 = 1 << 106;
uint256 internal constant _ROLE_107 = 1 << 107;
uint256 internal constant _ROLE_108 = 1 << 108;
uint256 internal constant _ROLE_109 = 1 << 109;
uint256 internal constant _ROLE_110 = 1 << 110;
uint256 internal constant _ROLE_111 = 1 << 111;
uint256 internal constant _ROLE_112 = 1 << 112;
uint256 internal constant _ROLE_113 = 1 << 113;
uint256 internal constant _ROLE_114 = 1 << 114;
uint256 internal constant _ROLE_115 = 1 << 115;
uint256 internal constant _ROLE_116 = 1 << 116;
uint256 internal constant _ROLE_117 = 1 << 117;
uint256 internal constant _ROLE_118 = 1 << 118;
uint256 internal constant _ROLE_119 = 1 << 119;
uint256 internal constant _ROLE_120 = 1 << 120;
uint256 internal constant _ROLE_121 = 1 << 121;
uint256 internal constant _ROLE_122 = 1 << 122;
uint256 internal constant _ROLE_123 = 1 << 123;
uint256 internal constant _ROLE_124 = 1 << 124;
uint256 internal constant _ROLE_125 = 1 << 125;
uint256 internal constant _ROLE_126 = 1 << 126;
uint256 internal constant _ROLE_127 = 1 << 127;
uint256 internal constant _ROLE_128 = 1 << 128;
uint256 internal constant _ROLE_129 = 1 << 129;
uint256 internal constant _ROLE_130 = 1 << 130;
uint256 internal constant _ROLE_131 = 1 << 131;
uint256 internal constant _ROLE_132 = 1 << 132;
uint256 internal constant _ROLE_133 = 1 << 133;
uint256 internal constant _ROLE_134 = 1 << 134;
uint256 internal constant _ROLE_135 = 1 << 135;
uint256 internal constant _ROLE_136 = 1 << 136;
uint256 internal constant _ROLE_137 = 1 << 137;
uint256 internal constant _ROLE_138 = 1 << 138;
uint256 internal constant _ROLE_139 = 1 << 139;
uint256 internal constant _ROLE_140 = 1 << 140;
uint256 internal constant _ROLE_141 = 1 << 141;
uint256 internal constant _ROLE_142 = 1 << 142;
uint256 internal constant _ROLE_143 = 1 << 143;
uint256 internal constant _ROLE_144 = 1 << 144;
uint256 internal constant _ROLE_145 = 1 << 145;
uint256 internal constant _ROLE_146 = 1 << 146;
uint256 internal constant _ROLE_147 = 1 << 147;
uint256 internal constant _ROLE_148 = 1 << 148;
uint256 internal constant _ROLE_149 = 1 << 149;
uint256 internal constant _ROLE_150 = 1 << 150;
uint256 internal constant _ROLE_151 = 1 << 151;
uint256 internal constant _ROLE_152 = 1 << 152;
uint256 internal constant _ROLE_153 = 1 << 153;
uint256 internal constant _ROLE_154 = 1 << 154;
uint256 internal constant _ROLE_155 = 1 << 155;
uint256 internal constant _ROLE_156 = 1 << 156;
uint256 internal constant _ROLE_157 = 1 << 157;
uint256 internal constant _ROLE_158 = 1 << 158;
uint256 internal constant _ROLE_159 = 1 << 159;
uint256 internal constant _ROLE_160 = 1 << 160;
uint256 internal constant _ROLE_161 = 1 << 161;
uint256 internal constant _ROLE_162 = 1 << 162;
uint256 internal constant _ROLE_163 = 1 << 163;
uint256 internal constant _ROLE_164 = 1 << 164;
uint256 internal constant _ROLE_165 = 1 << 165;
uint256 internal constant _ROLE_166 = 1 << 166;
uint256 internal constant _ROLE_167 = 1 << 167;
uint256 internal constant _ROLE_168 = 1 << 168;
uint256 internal constant _ROLE_169 = 1 << 169;
uint256 internal constant _ROLE_170 = 1 << 170;
uint256 internal constant _ROLE_171 = 1 << 171;
uint256 internal constant _ROLE_172 = 1 << 172;
uint256 internal constant _ROLE_173 = 1 << 173;
uint256 internal constant _ROLE_174 = 1 << 174;
uint256 internal constant _ROLE_175 = 1 << 175;
uint256 internal constant _ROLE_176 = 1 << 176;
uint256 internal constant _ROLE_177 = 1 << 177;
uint256 internal constant _ROLE_178 = 1 << 178;
uint256 internal constant _ROLE_179 = 1 << 179;
uint256 internal constant _ROLE_180 = 1 << 180;
uint256 internal constant _ROLE_181 = 1 << 181;
uint256 internal constant _ROLE_182 = 1 << 182;
uint256 internal constant _ROLE_183 = 1 << 183;
uint256 internal constant _ROLE_184 = 1 << 184;
uint256 internal constant _ROLE_185 = 1 << 185;
uint256 internal constant _ROLE_186 = 1 << 186;
uint256 internal constant _ROLE_187 = 1 << 187;
uint256 internal constant _ROLE_188 = 1 << 188;
uint256 internal constant _ROLE_189 = 1 << 189;
uint256 internal constant _ROLE_190 = 1 << 190;
uint256 internal constant _ROLE_191 = 1 << 191;
uint256 internal constant _ROLE_192 = 1 << 192;
uint256 internal constant _ROLE_193 = 1 << 193;
uint256 internal constant _ROLE_194 = 1 << 194;
uint256 internal constant _ROLE_195 = 1 << 195;
uint256 internal constant _ROLE_196 = 1 << 196;
uint256 internal constant _ROLE_197 = 1 << 197;
uint256 internal constant _ROLE_198 = 1 << 198;
uint256 internal constant _ROLE_199 = 1 << 199;
uint256 internal constant _ROLE_200 = 1 << 200;
uint256 internal constant _ROLE_201 = 1 << 201;
uint256 internal constant _ROLE_202 = 1 << 202;
uint256 internal constant _ROLE_203 = 1 << 203;
uint256 internal constant _ROLE_204 = 1 << 204;
uint256 internal constant _ROLE_205 = 1 << 205;
uint256 internal constant _ROLE_206 = 1 << 206;
uint256 internal constant _ROLE_207 = 1 << 207;
uint256 internal constant _ROLE_208 = 1 << 208;
uint256 internal constant _ROLE_209 = 1 << 209;
uint256 internal constant _ROLE_210 = 1 << 210;
uint256 internal constant _ROLE_211 = 1 << 211;
uint256 internal constant _ROLE_212 = 1 << 212;
uint256 internal constant _ROLE_213 = 1 << 213;
uint256 internal constant _ROLE_214 = 1 << 214;
uint256 internal constant _ROLE_215 = 1 << 215;
uint256 internal constant _ROLE_216 = 1 << 216;
uint256 internal constant _ROLE_217 = 1 << 217;
uint256 internal constant _ROLE_218 = 1 << 218;
uint256 internal constant _ROLE_219 = 1 << 219;
uint256 internal constant _ROLE_220 = 1 << 220;
uint256 internal constant _ROLE_221 = 1 << 221;
uint256 internal constant _ROLE_222 = 1 << 222;
uint256 internal constant _ROLE_223 = 1 << 223;
uint256 internal constant _ROLE_224 = 1 << 224;
uint256 internal constant _ROLE_225 = 1 << 225;
uint256 internal constant _ROLE_226 = 1 << 226;
uint256 internal constant _ROLE_227 = 1 << 227;
uint256 internal constant _ROLE_228 = 1 << 228;
uint256 internal constant _ROLE_229 = 1 << 229;
uint256 internal constant _ROLE_230 = 1 << 230;
uint256 internal constant _ROLE_231 = 1 << 231;
uint256 internal constant _ROLE_232 = 1 << 232;
uint256 internal constant _ROLE_233 = 1 << 233;
uint256 internal constant _ROLE_234 = 1 << 234;
uint256 internal constant _ROLE_235 = 1 << 235;
uint256 internal constant _ROLE_236 = 1 << 236;
uint256 internal constant _ROLE_237 = 1 << 237;
uint256 internal constant _ROLE_238 = 1 << 238;
uint256 internal constant _ROLE_239 = 1 << 239;
uint256 internal constant _ROLE_240 = 1 << 240;
uint256 internal constant _ROLE_241 = 1 << 241;
uint256 internal constant _ROLE_242 = 1 << 242;
uint256 internal constant _ROLE_243 = 1 << 243;
uint256 internal constant _ROLE_244 = 1 << 244;
uint256 internal constant _ROLE_245 = 1 << 245;
uint256 internal constant _ROLE_246 = 1 << 246;
uint256 internal constant _ROLE_247 = 1 << 247;
uint256 internal constant _ROLE_248 = 1 << 248;
uint256 internal constant _ROLE_249 = 1 << 249;
uint256 internal constant _ROLE_250 = 1 << 250;
uint256 internal constant _ROLE_251 = 1 << 251;
uint256 internal constant _ROLE_252 = 1 << 252;
uint256 internal constant _ROLE_253 = 1 << 253;
uint256 internal constant _ROLE_254 = 1 << 254;
uint256 internal constant _ROLE_255 = 1 << 255;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for storage of packed unsigned integers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibMap.sol)
library LibMap {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A uint8 map in storage.
struct Uint8Map {
mapping(uint256 => uint256) map;
}
/// @dev A uint16 map in storage.
struct Uint16Map {
mapping(uint256 => uint256) map;
}
/// @dev A uint32 map in storage.
struct Uint32Map {
mapping(uint256 => uint256) map;
}
/// @dev A uint40 map in storage. Useful for storing timestamps up to 34841 A.D.
struct Uint40Map {
mapping(uint256 => uint256) map;
}
/// @dev A uint64 map in storage.
struct Uint64Map {
mapping(uint256 => uint256) map;
}
/// @dev A uint128 map in storage.
struct Uint128Map {
mapping(uint256 => uint256) map;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the uint8 value at `index` in `map`.
function get(Uint8Map storage map, uint256 index) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(5, index))
result := byte(and(31, not(index)), sload(keccak256(0x00, 0x40)))
}
}
/// @dev Updates the uint8 value at `index` in `map`.
function set(Uint8Map storage map, uint256 index, uint8 value) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(5, index))
let s := keccak256(0x00, 0x40) // Storage slot.
mstore(0x00, sload(s))
mstore8(and(31, not(index)), value)
sstore(s, mload(0x00))
}
}
/// @dev Returns the uint16 value at `index` in `map`.
function get(Uint16Map storage map, uint256 index) internal view returns (uint16 result) {
result = uint16(map.map[index >> 4] >> ((index & 15) << 4));
}
/// @dev Updates the uint16 value at `index` in `map`.
function set(Uint16Map storage map, uint256 index, uint16 value) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(4, index))
let s := keccak256(0x00, 0x40) // Storage slot.
let o := shl(4, and(index, 15)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffff // Value mask.
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
/// @dev Returns the uint32 value at `index` in `map`.
function get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
result = uint32(map.map[index >> 3] >> ((index & 7) << 5));
}
/// @dev Updates the uint32 value at `index` in `map`.
function set(Uint32Map storage map, uint256 index, uint32 value) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(3, index))
let s := keccak256(0x00, 0x40) // Storage slot.
let o := shl(5, and(index, 7)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffffffff // Value mask.
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
/// @dev Returns the uint40 value at `index` in `map`.
function get(Uint40Map storage map, uint256 index) internal view returns (uint40 result) {
unchecked {
result = uint40(map.map[index / 6] >> ((index % 6) * 40));
}
}
/// @dev Updates the uint40 value at `index` in `map`.
function set(Uint40Map storage map, uint256 index, uint40 value) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, div(index, 6))
let s := keccak256(0x00, 0x40) // Storage slot.
let o := mul(40, mod(index, 6)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffffffffff // Value mask.
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
/// @dev Returns the uint64 value at `index` in `map`.
function get(Uint64Map storage map, uint256 index) internal view returns (uint64 result) {
result = uint64(map.map[index >> 2] >> ((index & 3) << 6));
}
/// @dev Updates the uint64 value at `index` in `map`.
function set(Uint64Map storage map, uint256 index, uint64 value) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(2, index))
let s := keccak256(0x00, 0x40) // Storage slot.
let o := shl(6, and(index, 3)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffffffffffffffff // Value mask.
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
/// @dev Returns the uint128 value at `index` in `map`.
function get(Uint128Map storage map, uint256 index) internal view returns (uint128 result) {
result = uint128(map.map[index >> 1] >> ((index & 1) << 7));
}
/// @dev Updates the uint128 value at `index` in `map`.
function set(Uint128Map storage map, uint256 index, uint128 value) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(1, index))
let s := keccak256(0x00, 0x40) // Storage slot.
let o := shl(7, and(index, 1)) // Storage slot offset (bits).
let v := sload(s) // Storage slot value.
let m := 0xffffffffffffffffffffffffffffffff // Value mask.
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "@solady/tokens/ERC20.sol";
import "@solady/auth/Ownable.sol";
import "@openzeppelin/token/ERC20/IERC20.sol";
import "./IUniswapV2Factory.sol";
import "./IUniswapV2Router02.sol";
contract Nchart is ERC20, Ownable {
struct User {
bool isBlacklisted;
bool isAutomatedMarketMaker;
bool isExcludedFromFees;
bool isExcludedFromMaxTransactionAmount;
}
struct Fees {
uint8 buy;
uint8 sell;
uint8 liquidity;
uint8 revShare;
uint8 team;
}
struct Settings {
bool limitsInEffect;
bool swapEnabled;
bool blacklistRenounced;
bool feeChangeRenounced;
bool tradingActive;
/// @dev Upon enabling trading, record the end block for bot protection fee
/// @dev This fee is a 90% fee that is reduced by 5% every block for 18 blocks.
uint216 endBlock;
}
IUniswapV2Router02 public immutable uniswapV2Router;
address public immutable uniswapV2Pair;
/// @dev Constant to access the allowance slot
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
uint256 public constant MAX_SUPPLY = 10_000_000 * 1e18;
uint256 public constant MIN_SWAP_AMOUNT = MAX_SUPPLY / 100_000; // 0.001%
uint256 public constant MAX_SWAP_AMOUNT = MAX_SUPPLY * 5 / 1_000; // 0.5%
uint256 public maxTransactionAmount;
uint256 public swapTokensAtAmount;
uint256 public maxWallet;
address public revShareWallet;
address public teamWallet;
bool private _swapping;
uint256 public tokensForBotProtection;
Fees public feeAmounts;
Settings private settings = Settings({
limitsInEffect: true,
swapEnabled: true,
blacklistRenounced: false,
feeChangeRenounced: false,
tradingActive: false,
endBlock: uint216(0)
});
mapping(address => User) private _users;
event ExcludeFromFees(address indexed account, bool isExcluded);
event ExcludeFromMaxTransaction(address indexed account, bool isExcluded);
event FailedSwapBackTransfer(address indexed destination, uint256 amount);
event FeesUpdated(uint8 buyFee, uint8 sellFee, uint8 revSharePercent, uint8 liquidityPercent, uint8 teamPercent);
event MaxTransactionAmountUpdated(uint256 newAmount, uint256 oldAmount);
event MaxWalletAmountUpdated(uint256 newAmount, uint256 oldAmount);
event RevShareWalletUpdated(address indexed newWallet, address indexed oldWallet);
event SetAutomatedMarketMakerPair(address indexed pair, bool value);
event SwapAndLiquify(uint256 tokensSwapped, uint256 ethReceived);
event SwapTokensAtAmountUpdated(uint256 newAmount, uint256 oldAmount);
event TeamWalletUpdated(address indexed newWallet, address indexed oldWallet);
event UpdateUniswapV2Router(address indexed newAddress, address indexed oldAddress);
error Nchart__BlacklistModificationDisabled();
error Nchart__BuyAmountGreaterThanMax();
error Nchart__CannotBlacklistLPPair();
error Nchart__CannotBlacklistRouter();
error Nchart__CannotRemovePairFromAMMs();
error Nchart__CannotSetWalletToAddressZero();
error Nchart__CannotTransferFromAddressZero();
error Nchart__CannotTransferToAddressZero();
error Nchart__ErrorWithdrawingEth();
error Nchart__FeeChangeRenounced();
error Nchart__MaxFeeFivePercent();
error Nchart__MaxTransactionTooLow();
error Nchart__MaxWalletAmountExceeded();
error Nchart__MaxWalletAmountTooLow();
error Nchart__OnlyOwner();
error Nchart__ReceiverBlacklisted();
error Nchart__ReceiverCannotBeAddressZero();
error Nchart__SellAmountGreaterThanMax();
error Nchart__SenderBlacklisted();
error Nchart__StuckEthWithdrawError();
error Nchart__SwapAmountGreaterThanMaximum();
error Nchart__SwapAmountLowerThanMinimum();
error Nchart__TokenAddressCannotBeAddressZero();
error Nchart__TradingNotActive();
constructor(address ownerWallet, address teamWallet_, address revShareWallet_, address routerAddress) {
_initializeOwner(ownerWallet);
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(routerAddress);
uniswapV2Router = _uniswapV2Router;
uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory()).createPair(address(this), _uniswapV2Router.WETH());
maxTransactionAmount = MAX_SUPPLY / 100; // 1%
maxWallet = MAX_SUPPLY / 100; // 1%
swapTokensAtAmount = MAX_SUPPLY * 5 / 10_000; // 0.05%
feeAmounts = Fees({buy: 5, sell: 5, revShare: 0, liquidity: 25, team: 75});
revShareWallet = revShareWallet_;
teamWallet = teamWallet_;
_users[teamWallet_] = User({
isExcludedFromFees: true,
isExcludedFromMaxTransactionAmount: true,
isAutomatedMarketMaker: false,
isBlacklisted: false
});
_users[address(this)] = User({
isExcludedFromFees: true,
isExcludedFromMaxTransactionAmount: true,
isAutomatedMarketMaker: false,
isBlacklisted: false
});
_users[address(0xdead)] = User({
isExcludedFromFees: true,
isExcludedFromMaxTransactionAmount: true,
isAutomatedMarketMaker: false,
isBlacklisted: false
});
_users[address(ownerWallet)] = User({
isExcludedFromFees: true,
isExcludedFromMaxTransactionAmount: true,
isAutomatedMarketMaker: false,
isBlacklisted: false
});
_users[address(uniswapV2Router)] = User({
isExcludedFromMaxTransactionAmount: true,
isAutomatedMarketMaker: false,
isExcludedFromFees: false,
isBlacklisted: false
});
_users[address(uniswapV2Pair)] = User({
isExcludedFromMaxTransactionAmount: true,
isAutomatedMarketMaker: true,
isExcludedFromFees: false,
isBlacklisted: false
});
_mint(ownerWallet, MAX_SUPPLY);
}
receive() external payable {}
function name() public pure override returns (string memory) {
return "Nchart";
}
function symbol() public pure override returns (string memory) {
return "CHART";
}
function enableTrading() public {
_requireIsOwner();
settings.endBlock = uint216(block.number) + 19;
settings.tradingActive = true;
}
// remove limits after token is stable
function removeLimits() external {
_requireIsOwner();
settings.limitsInEffect = false;
}
// change the minimum amount of tokens to sell from fees
function updateSwapTokensAtAmount(uint256 newAmount) external {
_requireIsOwner();
if (newAmount < MIN_SWAP_AMOUNT) {
revert Nchart__SwapAmountLowerThanMinimum();
}
if (newAmount > MAX_SWAP_AMOUNT) {
revert Nchart__SwapAmountGreaterThanMaximum();
}
uint256 oldSwapAmount = swapTokensAtAmount;
swapTokensAtAmount = newAmount;
emit SwapTokensAtAmountUpdated(newAmount, oldSwapAmount);
}
function updateMaxTransactionAmount(uint256 newAmount) external {
_requireIsOwner();
if (newAmount < MAX_SUPPLY * 5 / 1000) {
revert Nchart__MaxTransactionTooLow();
}
uint256 oldMaxTransactionAmount = maxTransactionAmount;
maxTransactionAmount = newAmount;
emit MaxTransactionAmountUpdated(newAmount, oldMaxTransactionAmount);
}
function updateMaxWalletAmount(uint256 newNum) external {
_requireIsOwner();
if (newNum < MAX_SUPPLY / 100) {
revert Nchart__MaxWalletAmountTooLow();
}
uint256 oldMaxWallet = maxWallet;
maxWallet = newNum;
emit MaxWalletAmountUpdated(newNum, oldMaxWallet);
}
// only use to disable contract sales if absolutely necessary (emergency use only)
function updateSwapEnabled(bool enabled) external {
_requireIsOwner();
settings.swapEnabled = enabled;
}
function updateBuyFees(uint8 revShareFee, uint8 liquidityFee, uint8 teamFee) external {
_requireIsOwner();
if (settings.feeChangeRenounced) {
revert Nchart__FeeChangeRenounced();
}
uint8 totalFees = revShareFee + liquidityFee + teamFee;
if (totalFees > 5) {
revert Nchart__MaxFeeFivePercent();
}
uint8 sellFee = feeAmounts.sell;
uint8 revPercent = revShareFee * 100 / totalFees;
uint8 liqPercent = liquidityFee * 100 / totalFees;
uint8 teamPercent = 100 - revPercent - liqPercent;
feeAmounts =
Fees({buy: totalFees, sell: sellFee, revShare: revPercent, liquidity: liqPercent, team: teamPercent});
emit FeesUpdated(totalFees, sellFee, revPercent, liqPercent, teamPercent);
}
function updateSellFees(uint8 revShareFee, uint8 liquidityFee, uint8 teamFee) external {
_requireIsOwner();
if (settings.feeChangeRenounced) {
revert Nchart__FeeChangeRenounced();
}
uint8 totalFees = revShareFee + liquidityFee + teamFee;
if (totalFees > 5) {
revert Nchart__MaxFeeFivePercent();
}
uint8 buyFee = feeAmounts.buy;
uint8 revPercent = revShareFee * 100 / totalFees;
uint8 liqPercent = liquidityFee * 100 / totalFees;
uint8 teamPercent = 100 - revPercent - liqPercent;
feeAmounts =
Fees({buy: buyFee, sell: totalFees, revShare: revPercent, liquidity: liqPercent, team: teamPercent});
emit FeesUpdated(buyFee, totalFees, revPercent, liqPercent, teamPercent);
}
function excludeFromFees(address account, bool excluded) external {
_requireIsOwner();
_users[account].isExcludedFromFees = excluded;
emit ExcludeFromFees(account, excluded);
}
function excludeFromMaxTransaction(address account, bool isExcluded) external {
_requireIsOwner();
_users[account].isExcludedFromMaxTransactionAmount = isExcluded;
emit ExcludeFromMaxTransaction(account, isExcluded);
}
function setAutomatedMarketMakerPair(address pair, bool value) external {
_requireIsOwner();
if (pair == uniswapV2Pair) {
revert Nchart__CannotRemovePairFromAMMs();
}
_users[pair].isAutomatedMarketMaker = value;
emit SetAutomatedMarketMakerPair(pair, value);
}
function updateRevShareWallet(address newWallet) external {
_requireIsOwner();
if (newWallet == address(0)) {
revert Nchart__CannotSetWalletToAddressZero();
}
address oldWallet = revShareWallet;
revShareWallet = newWallet;
emit RevShareWalletUpdated(newWallet, oldWallet);
}
function updateTeamWallet(address newWallet) external {
_requireIsOwner();
if (newWallet == address(0)) {
revert Nchart__CannotSetWalletToAddressZero();
}
address oldWallet = teamWallet;
teamWallet = newWallet;
emit TeamWalletUpdated(newWallet, oldWallet);
}
function withdrawStuckChart(uint256 amount) external {
_requireIsOwner();
uint256 transferAmount;
if (amount == 0) {
transferAmount = balanceOf(address(this));
} else {
transferAmount = amount;
}
super._transfer(address(this), msg.sender, transferAmount);
}
function withdrawStuckToken(address _token) external {
_requireIsOwner();
if (_token == address(0)) {
revert Nchart__TokenAddressCannotBeAddressZero();
}
uint256 _contractBalance = IERC20(_token).balanceOf(address(this));
IERC20(_token).transfer(msg.sender, _contractBalance);
}
function withdrawStuckEth() external {
_requireIsOwner();
(bool success,) = msg.sender.call{value: address(this).balance}("");
if (!success) {
revert Nchart__ErrorWithdrawingEth();
}
}
function renounceBlacklist() external {
_requireIsOwner();
settings.blacklistRenounced = true;
}
function renounceFeeChange() external {
_requireIsOwner();
settings.feeChangeRenounced = true;
}
function blacklist(address account) external {
_requireIsOwner();
if (settings.blacklistRenounced) {
revert Nchart__BlacklistModificationDisabled();
}
if (account == uniswapV2Pair) {
revert Nchart__CannotBlacklistLPPair();
}
if (account == address(uniswapV2Router)) {
revert Nchart__CannotBlacklistRouter();
}
_users[account].isBlacklisted = true;
}
// @dev unblacklist address; not affected by blacklistRenounced incase team wants to unblacklist v3 pools down the
// @dev road
function unblacklist(address account) external {
_requireIsOwner();
_users[account].isBlacklisted = false;
}
function isExcludedFromFees(address account) external view returns (bool) {
return _users[account].isExcludedFromFees;
}
function isExcludedFromMaxTransactionAmount(address account) external view returns (bool) {
return _users[account].isExcludedFromMaxTransactionAmount;
}
function isAutomatedMarketMakerPair(address pair) external view returns (bool) {
return _users[pair].isAutomatedMarketMaker;
}
function isBlacklisted(address account) external view returns (bool) {
return _users[account].isBlacklisted;
}
function isSwapEnabled() external view returns (bool) {
return settings.swapEnabled;
}
function isBlacklistRenounced() external view returns (bool) {
return settings.blacklistRenounced;
}
function isFeeChangeRenounced() external view returns (bool) {
return settings.feeChangeRenounced;
}
function isTradingActive() external view returns (bool) {
return settings.tradingActive;
}
function isLimitInEffect() external view returns (bool) {
return settings.limitsInEffect;
}
function transfer(address to, uint256 amount) public override returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
// Check allowance and reduce it if used, reverts with `InsufficientAllowance()` if not approved.
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if iszero(eq(allowance_, not(0))) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
_transfer(from, to, amount);
return true;
}
function _transfer(address from, address to, uint256 amount) internal override {
// Ignore mints, burns not enabled
if (from == address(0)) {
revert Nchart__CannotTransferFromAddressZero();
}
if (to == address(0)) {
revert Nchart__CannotTransferToAddressZero();
}
User memory fromData = _users[from];
User memory toData = _users[to];
Settings memory settingCache = settings;
if (!settingCache.tradingActive) {
if (!fromData.isExcludedFromFees) {
if (!toData.isExcludedFromFees) {
revert Nchart__TradingNotActive();
}
}
}
// Apply blacklist protection
if (fromData.isBlacklisted) {
revert Nchart__SenderBlacklisted();
}
if (toData.isBlacklisted) {
revert Nchart__ReceiverBlacklisted();
}
// If zero amount, continue
if (amount == 0) {
return;
}
bool excludedFromFees = fromData.isExcludedFromFees || toData.isExcludedFromFees;
// Cache transaction type for reference.
// 1 = Buy
// 2 = Sell
// 3 = Transfer
uint8 txType = 3;
if (fromData.isAutomatedMarketMaker) {
// Buys originate from the AMM pair
txType = 1;
} else if (toData.isAutomatedMarketMaker) {
// Sells send funds to AMM pair
txType = 2;
}
if (!_swapping) {
if (settingCache.limitsInEffect) {
//when buy
if (txType == 1 && !toData.isExcludedFromMaxTransactionAmount) {
if (amount > maxTransactionAmount) {
revert Nchart__BuyAmountGreaterThanMax();
}
if (amount + balanceOf(to) > maxWallet) {
revert Nchart__MaxWalletAmountExceeded();
}
}
//when sell
else if (txType == 2 && !fromData.isExcludedFromMaxTransactionAmount) {
if (amount > maxTransactionAmount) {
revert Nchart__SellAmountGreaterThanMax();
}
} else if (!toData.isExcludedFromMaxTransactionAmount) {
if (amount + balanceOf(to) > maxWallet) {
revert Nchart__MaxWalletAmountExceeded();
}
}
}
if (settingCache.swapEnabled) {
// Only sells will trigger the fee swap
if (txType == 2) {
if (balanceOf(address(this)) >= swapTokensAtAmount) {
_swapping = true;
_swapBack();
_swapping = false;
}
}
}
}
if (txType < 3) {
bool takeFee = !_swapping;
// if any account belongs to _isExcludedFromFee account then remove the fee
if (excludedFromFees) {
takeFee = false;
}
uint256 fees = 0;
// only take fees on buys/sells, do not take on wallet transfers
if (takeFee) {
Fees memory feeCache = feeAmounts;
// on sell
if (txType == 2) {
if (feeCache.sell > 0) {
fees = amount * feeCache.sell / 100;
}
}
// on buy
else if (txType == 1) {
if (feeCache.buy > 0) {
fees = amount * feeCache.buy / 100;
}
}
if (block.number < settingCache.endBlock) {
uint256 blocksLeft = settingCache.endBlock - block.number;
uint256 botFeeMultiplier = 90;
// Apply sniper protection - first 18 blocks have a fee reduced 5% each block.
if (blocksLeft < 18) {
botFeeMultiplier -= (5 * (18 - blocksLeft));
}
uint256 botFee = (amount * botFeeMultiplier) / 100;
super._transfer(from, teamWallet, botFee);
amount -= botFee;
tokensForBotProtection += botFee;
}
amount -= fees;
if (fees > 0) {
super._transfer(from, address(this), fees);
}
}
}
super._transfer(from, to, amount);
}
function _swapTokensForEth(uint256 tokenAmount) internal {
// generate the uniswap pair path of token -> weth
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router.WETH();
_approve(address(this), address(uniswapV2Router), tokenAmount);
// make the swap
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0, // accept any amount of ETH
path,
address(this),
block.timestamp
);
}
function _addLiquidity(uint256 tokenAmount, uint256 ethAmount) internal {
// approve token transfer to cover all possible scenarios
_approve(address(this), address(uniswapV2Router), tokenAmount);
// add the liquidity
uniswapV2Router.addLiquidityETH{value: ethAmount}(
address(this),
tokenAmount,
0, // slippage is unavoidable
0, // slippage is unavoidable
owner(),
block.timestamp
);
}
function _swapBack() internal {
// Cache values
uint256 contractBalance = balanceOf(address(this));
Fees memory feeCache = feeAmounts;
bool success;
if (contractBalance == 0) {
return;
}
// Prevent too many tokens from being swapped
uint256 maxAmount = swapTokensAtAmount * 20;
if (contractBalance > maxAmount) {
contractBalance = maxAmount;
}
uint256 liquidityAmount = contractBalance * feeCache.liquidity / 100;
// Halve the amount of liquidity tokens
uint256 liquidityTokens = liquidityAmount - (liquidityAmount / 2);
uint256 amountToSwapForETH = contractBalance - liquidityTokens;
uint256 initialETHBalance = address(this).balance;
_swapTokensForEth(amountToSwapForETH);
uint256 ethBalance = address(this).balance - initialETHBalance;
uint256 ethForRevShare = ethBalance * feeCache.revShare / 100;
uint256 ethForTeam = ethBalance * feeCache.team / 100;
uint256 ethForLiquidity = ethBalance - ethForRevShare - ethForTeam;
if (liquidityTokens > 0 && ethForLiquidity > 0) {
_addLiquidity(liquidityTokens, ethForLiquidity);
emit SwapAndLiquify(amountToSwapForETH, ethForLiquidity);
}
address teamWallet_ = teamWallet;
(success,) = address(teamWallet_).call{value: ethForTeam}("");
if (!success) {
emit FailedSwapBackTransfer(teamWallet_, ethForTeam);
}
if (ethForRevShare > 0) {
(success,) = address(revShareWallet).call{value: ethForRevShare}("");
if (!success) {
emit FailedSwapBackTransfer(revShareWallet, ethForRevShare);
}
}
}
function _requireIsOwner() internal view {
if (msg.sender != owner()) {
revert Nchart__OnlyOwner();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
import "./IUniswapV2Router01.sol";
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) external returns (uint256 amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(not(_OWNER_SLOT_NOT), newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := not(_OWNER_SLOT_NOT)
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(not(_OWNER_SLOT_NOT))
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// The ERC20 standard allows minting and transferring to and from the zero address,
/// minting and transferring zero tokens, as well as self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
abstract contract ERC20 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The total supply has overflowed.
error TotalSupplyOverflow();
/// @dev The allowance has overflowed.
error AllowanceOverflow();
/// @dev The allowance has underflowed.
error AllowanceUnderflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Insufficient allowance.
error InsufficientAllowance();
/// @dev The permit is invalid.
error InvalidPermit();
/// @dev The permit has expired.
error PermitExpired();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The storage slot for the total supply.
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
/// @dev The balance slot of `owner` is given by:
/// ```
/// mstore(0x0c, _BALANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev The allowance slot of (`owner`, `spender`) is given by:
/// ```
/// mstore(0x20, spender)
/// mstore(0x0c, _ALLOWANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let allowanceSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev The nonce slot of `owner` is given by:
/// ```
/// mstore(0x0c, _NONCES_SLOT_SEED)
/// mstore(0x00, owner)
/// let nonceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns (string memory);
/// @dev Returns the decimals places of the token.
function decimals() public view virtual returns (uint8) {
return 18;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Atomically increases the allowance granted to `spender` by the caller.
///
/// Emits a {Approval} event.
function increaseAllowance(address spender, uint256 difference) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
let allowanceSlot := keccak256(0x0c, 0x34)
let allowanceBefore := sload(allowanceSlot)
// Add to the allowance.
let allowanceAfter := add(allowanceBefore, difference)
// Revert upon overflow.
if lt(allowanceAfter, allowanceBefore) {
mstore(0x00, 0xf9067066) // `AllowanceOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated allowance.
sstore(allowanceSlot, allowanceAfter)
// Emit the {Approval} event.
mstore(0x00, allowanceAfter)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Atomically decreases the allowance granted to `spender` by the caller.
///
/// Emits a {Approval} event.
function decreaseAllowance(address spender, uint256 difference) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
let allowanceSlot := keccak256(0x0c, 0x34)
let allowanceBefore := sload(allowanceSlot)
// Revert if will underflow.
if lt(allowanceBefore, difference) {
mstore(0x00, 0x8301ab38) // `AllowanceUnderflow()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
let allowanceAfter := sub(allowanceBefore, difference)
sstore(allowanceSlot, allowanceAfter)
// Emit the {Approval} event.
mstore(0x00, allowanceAfter)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if iszero(eq(allowance_, not(0))) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-2612 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the current nonce for `owner`.
/// This value is used to compute the signature for EIP-2612 permit.
function nonces(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
/// authorized by a signed approval by `owner`.
///
/// Emits a {Approval} event.
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 domainSeparator = DOMAIN_SEPARATOR();
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
let m := mload(0x40)
// Revert if the block timestamp greater than `deadline`.
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
// Increment and store the updated nonce.
sstore(nonceSlot, add(nonceValue, 1))
// Prepare the inner hash.
// `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
// forgefmt: disable-next-item
mstore(m, 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9)
mstore(add(m, 0x20), owner)
mstore(add(m, 0x40), spender)
mstore(add(m, 0x60), value)
mstore(add(m, 0x80), nonceValue)
mstore(add(m, 0xa0), deadline)
// Prepare the outer hash.
mstore(0, 0x1901)
mstore(0x20, domainSeparator)
mstore(0x40, keccak256(m, 0xc0))
// Prepare the ecrecover calldata.
mstore(0, keccak256(0x1e, 0x42))
mstore(0x20, and(0xff, v))
mstore(0x40, r)
mstore(0x60, s)
pop(staticcall(gas(), 1, 0, 0x80, 0x20, 0x20))
// If the ecrecover fails, the returndatasize will be 0x00,
// `owner` will be be checked if it equals the hash at 0x00,
// which evaluates to false (i.e. 0), and we will revert.
// If the ecrecover succeeds, the returndatasize will be 0x20,
// `owner` will be compared against the returned address at 0x20.
if iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
revert(0x1c, 0x04)
}
// Compute the allowance slot and store the value.
// The `owner` is already at slot 0x20.
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
// Emit the {Approval} event.
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns the EIP-2612 domains separator.
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40) // Grab the free memory pointer.
}
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
bytes32 nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
let m := result
// `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
// forgefmt: disable-next-item
mstore(m, 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f)
mstore(add(m, 0x20), nameHash)
// `keccak256("1")`.
// forgefmt: disable-next-item
mstore(add(m, 0x40), 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
/// @solidity memory-safe-assembly
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
// Revert if the total supply overflows.
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Subtract and store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Moves `amount` of tokens from `from` to `to`.
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL ALLOWANCE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if iszero(eq(allowance_, not(0))) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let owner_ := shl(96, owner)
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any transfer of tokens.
/// This includes minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/// @dev Hook that is called after any transfer of tokens.
/// This includes minting and burning.
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external returns (address);
function WETH() external returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB);
function removeLiquidityETH(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
) external returns (uint256 amountToken, uint256 amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountA, uint256 amountB);
function removeLiquidityETHWithPermit(
address token,
uint256 liquidity,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 amountToken, uint256 amountETH);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline)
external
payable
returns (uint256[] memory amounts);
function swapTokensForExactETH(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to, uint256 deadline)
external
payable
returns (uint256[] memory amounts);
function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB);
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
external
pure
returns (uint256 amountOut);
function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
external
pure
returns (uint256 amountIn);
function getAmountsOut(uint256 amountIn, address[] calldata path)
external
view
returns (uint256[] memory amounts);
function getAmountsIn(uint256 amountOut, address[] calldata path)
external
view
returns (uint256[] memory amounts);
}{
"remappings": [
"@openzeppelin/=lib/openzeppelin-contracts/contracts/",
"@solady/=lib/solady/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solady/=lib/solady/",
"lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/",
"lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"lib/openzeppelin-contracts:erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/",
"lib/openzeppelin-contracts:openzeppelin/=lib/openzeppelin-contracts/contracts/",
"lib/solady:ds-test/=lib/solady/lib/ds-test/src/",
"lib/solady:forge-std/=lib/solady/test/utils/forge-std/"
],
"optimizer": {
"enabled": true,
"runs": 500000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address payable","name":"chart_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"router_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"SubscriptionManager__BurnPercentMustBeGreaterThan50","type":"error"},{"inputs":[],"name":"SubscriptionManager__BurnPercentMustBeLessThan100","type":"error"},{"inputs":[],"name":"SubscriptionManager__CanOnlyIncreaseExpiration","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotReduceMoreThanSubscriptionPrice","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotReferSelf","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotRegisterAddressZero","type":"error"},{"inputs":[],"name":"SubscriptionManager__ErrorRetrievingPriceFromDataFeed","type":"error"},{"inputs":[],"name":"SubscriptionManager__ErrorSendingKeeperFunds","type":"error"},{"inputs":[{"internalType":"uint256","name":"msgValue","type":"uint256"},{"internalType":"uint256","name":"ethRequired","type":"uint256"}],"name":"SubscriptionManager__InvalidETHAmountProvided","type":"error"},{"inputs":[],"name":"SubscriptionManager__MaxFiftyPercentReferralPercent","type":"error"},{"inputs":[],"name":"SubscriptionManager__MustProvideAtLeastOneAddress","type":"error"},{"inputs":[],"name":"SubscriptionManager__OnlyOwner","type":"error"},{"inputs":[],"name":"SubscriptionManager__UseRegisterAddressesFunction","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"newPercent","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"oldPercent","type":"uint8"}],"name":"BurnPercentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldAmount","type":"uint256"}],"name":"ReferralFeePriceReductionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"newPercent","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"oldPercent","type":"uint8"}],"name":"ReferralPercentUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newLength","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldLength","type":"uint256"}],"name":"SubscriptionLengthUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"subscriber","type":"address"},{"indexed":false,"internalType":"uint40","name":"expirationTimestamp","type":"uint40"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"SubscriptionPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldPrice","type":"uint256"}],"name":"SubscriptionPriceUpdated","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"KEEPER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"burnETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnPercent","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chart","outputs":[{"internalType":"contract Nchart","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getExpiration","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newKeeper","type":"address"}],"name":"grantKeeperRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isAddressRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralFeePriceReduction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralPercent","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"address","name":"referrer","type":"address"}],"name":"registerAddresses","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"toRevoke","type":"address"}],"name":"revokeKeeperRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"newPercent","type":"uint8"}],"name":"setBurnPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"newExpiration","type":"uint40"},{"internalType":"address","name":"user","type":"address"}],"name":"setExpirationTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAmount","type":"uint256"}],"name":"setReferralFeePriceReduction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"newPercent","type":"uint8"}],"name":"setReferralPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"newSubscriptionLength","type":"uint40"}],"name":"setSubscriptionLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newSubscriptionPrice","type":"uint256"}],"name":"setSubscriptionPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"subscriptionLength","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"subscriptionPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c060405266354a6ba7a1800060015566038d7ea4c680006002556003805461ffff60281b191666056300000000001790553480156200003e57600080fd5b5060405162001b2f38038062001b2f8339810160408190526200006191620000dc565b6200006c8262000087565b6001600160a01b0392831660805290911660a0525062000130565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6001600160a01b0381168114620000d957600080fd5b50565b600080600060608486031215620000f257600080fd5b8351620000ff81620000c3565b60208501519093506200011281620000c3565b60408501519092506200012581620000c3565b809150509250925092565b60805160a0516119c46200016b600039600081816106ac015281816109e90152610b440152600081816106520152610ac701526119c46000f3fe6080604052600436106102025760003560e01c806354d1f13d1161011d578063bdc8e54c116100b0578063e52ea2621161007f578063f2fde38b11610064578063f2fde38b14610687578063f887ea401461069a578063fee81cf4146106ce57610239565b8063e52ea26214610640578063f04e283e1461067457610239565b8063bdc8e54c146105ca578063c6b73b1b146105e0578063cc1f071a14610600578063d28720ba1461062057610239565b80638da5cb5b116100ec5780638da5cb5b1461052457806390bdd3421461057957806397f4c27e1461058c578063aa04732c146105ac57610239565b806354d1f13d146104de578063715018a6146104e6578063755a2433146104ee5780637fa19c1d1461050e57610239565b80632de94807116101955780634106d274116101645780634106d2741461043e5780634a4ee7b11461045e5780634ec70a4814610471578063514e62fc146104a757610239565b80632de9480714610365578063343dfb7e146103a6578063364bc15a146103ca57806338b6a3a1146103df57610239565b80631c10893f116101d15780631c10893f146102fa5780631cd64df41461030d5780631cf7db491461033d578063256929621461035d57610239565b806303807ee51461026b57806316cd8400146102a5578063183a4f6e146102c75780631a5ee779146102da57610239565b36610239576040517f2dc840f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2dc840f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801561027757600080fd5b5060035461028e9065010000000000900460ff1681565b60405160ff90911681526020015b60405180910390f35b3480156102b157600080fd5b506102c56102c0366004611647565b610701565b005b6102c56102d5366004611647565b61078a565b3480156102e657600080fd5b506102c56102f5366004611647565b610797565b6102c5610308366004611682565b6107dd565b34801561031957600080fd5b5061032d610328366004611682565b6107f3565b604051901515815260200161029c565b34801561034957600080fd5b506102c56103583660046116c8565b610812565b6102c5610888565b34801561037157600080fd5b506103986103803660046116ea565b638b78c6d8600c908152600091909152602090205490565b60405190815260200161029c565b3480156103b257600080fd5b5060035461028e906601000000000000900460ff1681565b3480156103d657600080fd5b50610398600181565b3480156103eb57600080fd5b5061032d6103fa3660046116ea565b600673ffffffffffffffffffffffffffffffffffffffff821681810460009081526020819052604081205490929091066028021c64ffffffffff1642111592915050565b34801561044a57600080fd5b506102c56104593660046116ea565b6108d8565b6102c561046c366004611682565b6108e3565b34801561047d57600080fd5b5061049161048c3660046116ea565b6108f5565b60405164ffffffffff909116815260200161029c565b3480156104b357600080fd5b5061032d6104c2366004611682565b638b78c6d8600c90815260009290925260209091205416151590565b6102c561092e565b6102c561096a565b3480156104fa57600080fd5b506102c5610509366004611647565b61097e565b34801561051a57600080fd5b5061039860025481565b34801561053057600080fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927545b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161029c565b6102c5610587366004611707565b610c41565b34801561059857600080fd5b506102c56105a736600461178d565b611125565b3480156105b857600080fd5b506003546104919064ffffffffff1681565b3480156105d657600080fd5b5061039860015481565b3480156105ec57600080fd5b506102c56105fb36600461178d565b61121f565b34801561060c57600080fd5b506102c561061b3660046116ea565b6112dc565b34801561062c57600080fd5b506102c561063b3660046117b0565b6112e7565b34801561064c57600080fd5b506105547f000000000000000000000000000000000000000000000000000000000000000081565b6102c56106823660046116ea565b6113b5565b6102c56106953660046116ea565b6113f2565b3480156106a657600080fd5b506105547f000000000000000000000000000000000000000000000000000000000000000081565b3480156106da57600080fd5b506103986106e93660046116ea565b63389a75e1600c908152600091909152602090205490565b610709611419565b6001548110610744576040517f3a3030f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280549082905560408051838152602081018390527ff84e671f306567a02e51640cda3cd6a82c360263796039994f2424a914bdead191015b60405180910390a15050565b610794338261149f565b50565b61079f611419565b600180549082905560408051838152602081018390527ff68b6199a6c7e5df3005a4142808bc836237ab35986a197b454750d532b56806910161077e565b6107e56114ab565b6107ef82826114e1565b5050565b638b78c6d8600c90815260008390526020902054811681145b92915050565b61081a611419565b6003805464ffffffffff8381167fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000008316811790935560408051938452911660208301819052917f85d5275f0008a2c2a0f3f1b37202f5c11b25d8800171d866f72c12d7026c68c7910161077e565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6107948160016107dd565b6108eb6114ab565b6107ef828261149f565b600673ffffffffffffffffffffffffffffffffffffffff821681810460009081526020819052604081205490929091066028021c61080c565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6109726114ab565b61097c60006114ed565b565b6109886001611553565b60035447906000906064906109a99065010000000000900460ff1684611816565b6109b3919061182d565b905060006109c18284611868565b60408051600280825260608201835292935060009290916020830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad5c46486040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a78919061187b565b81600081518110610a8b57610a8b611898565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250507f000000000000000000000000000000000000000000000000000000000000000081600181518110610af957610af9611898565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920101526040517fb6f9de950000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000009091169063b6f9de95908590610b85908990869061dead9042906004016118c7565b6000604051808303818588803b158015610b9e57600080fd5b505af1158015610bb2573d6000803e3d6000fd5b5050604051600093503392508591508381818185875af1925050503d8060008114610bf9576040519150601f19603f3d011682016040523d82523d6000602084013e610bfe565b606091505b5050905080610c39576040517fc1cfa45800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b816000819003610c7d576040517f9d1e5d8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff831603610ccc576040517f036b759100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154600073ffffffffffffffffffffffffffffffffffffffff8416610cf3576000610d01565b82600254610d019190611816565b90506000610d0f8385611816565b600673ffffffffffffffffffffffffffffffffffffffff8716818104600090815260208190526040812054939450929190066028021c64ffffffffff164211610d91576003546601000000000000900460ff1615610d9157600354606490610d84906601000000000000900460ff1684611816565b610d8e919061182d565b90505b8215610da457610da18383611868565b91505b813414610dea576040517fc07dca620000000000000000000000000000000000000000000000000000000081523460048201526024810183905260440160405180910390fd5b60035464ffffffffff166000819003610ef85760005b86811015610ef25760008a8a83818110610e1c57610e1c611898565b9050602002016020810190610e3191906116ea565b9050610e3c816115a1565b610e9360008273ffffffffffffffffffffffffffffffffffffffff1664ffffffffff8260205260068204600052604060002060068306602802815464ffffffffff8482841c188116831b8218845550505050505050565b6040805164ffffffffff81526020810189905273ffffffffffffffffffffffffffffffffffffffff8316917fef9739395821419392cdc21a614130a3edef45e59de8e4a923feac9bf49f6efa910160405180910390a250600101610e00565b5061107f565b6000610f098264ffffffffff61194b565b905060005b8781101561107c5760008b8b83818110610f2a57610f2a611898565b9050602002016020810190610f3f91906116ea565b9050610f4a816115a1565b600673ffffffffffffffffffffffffffffffffffffffff821681810460009081526020819052604081205490929091066028021c90504264ffffffffff8082169083161015610fc7578464ffffffffff168164ffffffffff161115610fb65764ffffffffff9150610fef565b610fc08682611970565b9150610fef565b8464ffffffffff168264ffffffffff161015610fe65790850190610fef565b64ffffffffff91505b60006020818152600673ffffffffffffffffffffffffffffffffffffffff86168181048452604093849020805464ffffffffff93830660280281811c89188516901b189055835191861682529181018d9052825191927fef9739395821419392cdc21a614130a3edef45e59de8e4a923feac9bf49f6efa92918290030190a2836001019350505050610f0e565b50505b811561111a5760405173ffffffffffffffffffffffffffffffffffffffff88169083156108fc029084906000818181858888f193505050501580156110c8573d6000803e3d6000fd5b508673ffffffffffffffffffffffffffffffffffffffff167f6292f4f27d8047af4537a4bd86906de0e2c880c20086f7f58b81eacfeb6391268360405161111191815260200190565b60405180910390a25b505050505050505050565b61112d611419565b60648160ff16111561116b576040517f97fd339b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60328160ff1610156111a9576040517f117cc36400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805460ff838116650100000000008181027fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff85161790945560408051918252939092041660208201819052917fc24a15357571f178f6f231afed02d31e3b82347a1401f9829a99264112d9e1ae910161077e565b611227611419565b60328160ff161115611265576040517f5b58f99200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805460ff83811666010000000000008181027fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff85161790945560408051918252939092041660208201819052917f769040d37c1ea0e87874f1175f04500dffbf7eef65fb17a67bb24b18a110a5ef910161077e565b6107948160016108e3565b6112ef611419565b600673ffffffffffffffffffffffffffffffffffffffff82168181046000908152602081905260409020549190066028021c64ffffffffff168264ffffffffff161015611368576040517f3442f50400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006020819052600673ffffffffffffffffffffffffffffffffffffffff83168181048352604090922080546028929093069190910282811c851864ffffffffff16901b90911890555050565b6113bd6114ab565b63389a75e1600c52806000526020600c2080544211156113e557636f5e88186000526004601cfd5b60009055610794816114ed565b6113fa6114ab565b8060601b61141057637448fbae6000526004601cfd5b610794816114ed565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461097c576040517f2acd5d9d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107ef828260006115ee565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754331461097c576382b429006000526004601cfd5b6107ef828260016115ee565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805473ffffffffffffffffffffffffffffffffffffffff9092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b638b78c6d8600c5233600052806020600c205416610794577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314610794576382b429006000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff8116610794576040517f32ae9d2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b638b78c6d8600c52826000526020600c20805483811783611610575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b60006020828403121561165957600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461079457600080fd5b6000806040838503121561169557600080fd5b82356116a081611660565b946020939093013593505050565b803564ffffffffff811681146116c357600080fd5b919050565b6000602082840312156116da57600080fd5b6116e3826116ae565b9392505050565b6000602082840312156116fc57600080fd5b81356116e381611660565b60008060006040848603121561171c57600080fd5b833567ffffffffffffffff8082111561173457600080fd5b818601915086601f83011261174857600080fd5b81358181111561175757600080fd5b8760208260051b850101111561176c57600080fd5b6020928301955093505084013561178281611660565b809150509250925092565b60006020828403121561179f57600080fd5b813560ff811681146116e357600080fd5b600080604083850312156117c357600080fd5b6117cc836116ae565b915060208301356117dc81611660565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761080c5761080c6117e7565b600082611863577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8181038181111561080c5761080c6117e7565b60006020828403121561188d57600080fd5b81516116e381611660565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060808201868352602060808185015281875180845260a086019150828901935060005b8181101561191e57845173ffffffffffffffffffffffffffffffffffffffff16835293830193918301916001016118ec565b505073ffffffffffffffffffffffffffffffffffffffff9690961660408501525050506060015292915050565b64ffffffffff828116828216039080821115611969576119696117e7565b5092915050565b64ffffffffff818116838216019080821115611969576119696117e756fea26469706673582212203796a22bebec0a1bc320ab1ba5eeff40cda41d1cdf8f2754d91b9f5f2156c89664736f6c63430008150033000000000000000000000000f62ac0fcae17f9195280ced4de978313effe2daa000000000000000000000000ae010a324281de301d1bfb2611ef8873d88436220000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
Deployed Bytecode
0x6080604052600436106102025760003560e01c806354d1f13d1161011d578063bdc8e54c116100b0578063e52ea2621161007f578063f2fde38b11610064578063f2fde38b14610687578063f887ea401461069a578063fee81cf4146106ce57610239565b8063e52ea26214610640578063f04e283e1461067457610239565b8063bdc8e54c146105ca578063c6b73b1b146105e0578063cc1f071a14610600578063d28720ba1461062057610239565b80638da5cb5b116100ec5780638da5cb5b1461052457806390bdd3421461057957806397f4c27e1461058c578063aa04732c146105ac57610239565b806354d1f13d146104de578063715018a6146104e6578063755a2433146104ee5780637fa19c1d1461050e57610239565b80632de94807116101955780634106d274116101645780634106d2741461043e5780634a4ee7b11461045e5780634ec70a4814610471578063514e62fc146104a757610239565b80632de9480714610365578063343dfb7e146103a6578063364bc15a146103ca57806338b6a3a1146103df57610239565b80631c10893f116101d15780631c10893f146102fa5780631cd64df41461030d5780631cf7db491461033d578063256929621461035d57610239565b806303807ee51461026b57806316cd8400146102a5578063183a4f6e146102c75780631a5ee779146102da57610239565b36610239576040517f2dc840f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2dc840f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34801561027757600080fd5b5060035461028e9065010000000000900460ff1681565b60405160ff90911681526020015b60405180910390f35b3480156102b157600080fd5b506102c56102c0366004611647565b610701565b005b6102c56102d5366004611647565b61078a565b3480156102e657600080fd5b506102c56102f5366004611647565b610797565b6102c5610308366004611682565b6107dd565b34801561031957600080fd5b5061032d610328366004611682565b6107f3565b604051901515815260200161029c565b34801561034957600080fd5b506102c56103583660046116c8565b610812565b6102c5610888565b34801561037157600080fd5b506103986103803660046116ea565b638b78c6d8600c908152600091909152602090205490565b60405190815260200161029c565b3480156103b257600080fd5b5060035461028e906601000000000000900460ff1681565b3480156103d657600080fd5b50610398600181565b3480156103eb57600080fd5b5061032d6103fa3660046116ea565b600673ffffffffffffffffffffffffffffffffffffffff821681810460009081526020819052604081205490929091066028021c64ffffffffff1642111592915050565b34801561044a57600080fd5b506102c56104593660046116ea565b6108d8565b6102c561046c366004611682565b6108e3565b34801561047d57600080fd5b5061049161048c3660046116ea565b6108f5565b60405164ffffffffff909116815260200161029c565b3480156104b357600080fd5b5061032d6104c2366004611682565b638b78c6d8600c90815260009290925260209091205416151590565b6102c561092e565b6102c561096a565b3480156104fa57600080fd5b506102c5610509366004611647565b61097e565b34801561051a57600080fd5b5061039860025481565b34801561053057600080fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927545b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161029c565b6102c5610587366004611707565b610c41565b34801561059857600080fd5b506102c56105a736600461178d565b611125565b3480156105b857600080fd5b506003546104919064ffffffffff1681565b3480156105d657600080fd5b5061039860015481565b3480156105ec57600080fd5b506102c56105fb36600461178d565b61121f565b34801561060c57600080fd5b506102c561061b3660046116ea565b6112dc565b34801561062c57600080fd5b506102c561063b3660046117b0565b6112e7565b34801561064c57600080fd5b506105547f000000000000000000000000f62ac0fcae17f9195280ced4de978313effe2daa81565b6102c56106823660046116ea565b6113b5565b6102c56106953660046116ea565b6113f2565b3480156106a657600080fd5b506105547f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d81565b3480156106da57600080fd5b506103986106e93660046116ea565b63389a75e1600c908152600091909152602090205490565b610709611419565b6001548110610744576040517f3a3030f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280549082905560408051838152602081018390527ff84e671f306567a02e51640cda3cd6a82c360263796039994f2424a914bdead191015b60405180910390a15050565b610794338261149f565b50565b61079f611419565b600180549082905560408051838152602081018390527ff68b6199a6c7e5df3005a4142808bc836237ab35986a197b454750d532b56806910161077e565b6107e56114ab565b6107ef82826114e1565b5050565b638b78c6d8600c90815260008390526020902054811681145b92915050565b61081a611419565b6003805464ffffffffff8381167fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000008316811790935560408051938452911660208301819052917f85d5275f0008a2c2a0f3f1b37202f5c11b25d8800171d866f72c12d7026c68c7910161077e565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b6107948160016107dd565b6108eb6114ab565b6107ef828261149f565b600673ffffffffffffffffffffffffffffffffffffffff821681810460009081526020819052604081205490929091066028021c61080c565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6109726114ab565b61097c60006114ed565b565b6109886001611553565b60035447906000906064906109a99065010000000000900460ff1684611816565b6109b3919061182d565b905060006109c18284611868565b60408051600280825260608201835292935060009290916020830190803683370190505090507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d73ffffffffffffffffffffffffffffffffffffffff1663ad5c46486040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a78919061187b565b81600081518110610a8b57610a8b611898565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250507f000000000000000000000000f62ac0fcae17f9195280ced4de978313effe2daa81600181518110610af957610af9611898565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920101526040517fb6f9de950000000000000000000000000000000000000000000000000000000081527f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d9091169063b6f9de95908590610b85908990869061dead9042906004016118c7565b6000604051808303818588803b158015610b9e57600080fd5b505af1158015610bb2573d6000803e3d6000fd5b5050604051600093503392508591508381818185875af1925050503d8060008114610bf9576040519150601f19603f3d011682016040523d82523d6000602084013e610bfe565b606091505b5050905080610c39576040517fc1cfa45800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b816000819003610c7d576040517f9d1e5d8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff831603610ccc576040517f036b759100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154600073ffffffffffffffffffffffffffffffffffffffff8416610cf3576000610d01565b82600254610d019190611816565b90506000610d0f8385611816565b600673ffffffffffffffffffffffffffffffffffffffff8716818104600090815260208190526040812054939450929190066028021c64ffffffffff164211610d91576003546601000000000000900460ff1615610d9157600354606490610d84906601000000000000900460ff1684611816565b610d8e919061182d565b90505b8215610da457610da18383611868565b91505b813414610dea576040517fc07dca620000000000000000000000000000000000000000000000000000000081523460048201526024810183905260440160405180910390fd5b60035464ffffffffff166000819003610ef85760005b86811015610ef25760008a8a83818110610e1c57610e1c611898565b9050602002016020810190610e3191906116ea565b9050610e3c816115a1565b610e9360008273ffffffffffffffffffffffffffffffffffffffff1664ffffffffff8260205260068204600052604060002060068306602802815464ffffffffff8482841c188116831b8218845550505050505050565b6040805164ffffffffff81526020810189905273ffffffffffffffffffffffffffffffffffffffff8316917fef9739395821419392cdc21a614130a3edef45e59de8e4a923feac9bf49f6efa910160405180910390a250600101610e00565b5061107f565b6000610f098264ffffffffff61194b565b905060005b8781101561107c5760008b8b83818110610f2a57610f2a611898565b9050602002016020810190610f3f91906116ea565b9050610f4a816115a1565b600673ffffffffffffffffffffffffffffffffffffffff821681810460009081526020819052604081205490929091066028021c90504264ffffffffff8082169083161015610fc7578464ffffffffff168164ffffffffff161115610fb65764ffffffffff9150610fef565b610fc08682611970565b9150610fef565b8464ffffffffff168264ffffffffff161015610fe65790850190610fef565b64ffffffffff91505b60006020818152600673ffffffffffffffffffffffffffffffffffffffff86168181048452604093849020805464ffffffffff93830660280281811c89188516901b189055835191861682529181018d9052825191927fef9739395821419392cdc21a614130a3edef45e59de8e4a923feac9bf49f6efa92918290030190a2836001019350505050610f0e565b50505b811561111a5760405173ffffffffffffffffffffffffffffffffffffffff88169083156108fc029084906000818181858888f193505050501580156110c8573d6000803e3d6000fd5b508673ffffffffffffffffffffffffffffffffffffffff167f6292f4f27d8047af4537a4bd86906de0e2c880c20086f7f58b81eacfeb6391268360405161111191815260200190565b60405180910390a25b505050505050505050565b61112d611419565b60648160ff16111561116b576040517f97fd339b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60328160ff1610156111a9576040517f117cc36400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805460ff838116650100000000008181027fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff85161790945560408051918252939092041660208201819052917fc24a15357571f178f6f231afed02d31e3b82347a1401f9829a99264112d9e1ae910161077e565b611227611419565b60328160ff161115611265576040517f5b58f99200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805460ff83811666010000000000008181027fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff85161790945560408051918252939092041660208201819052917f769040d37c1ea0e87874f1175f04500dffbf7eef65fb17a67bb24b18a110a5ef910161077e565b6107948160016108e3565b6112ef611419565b600673ffffffffffffffffffffffffffffffffffffffff82168181046000908152602081905260409020549190066028021c64ffffffffff168264ffffffffff161015611368576040517f3442f50400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006020819052600673ffffffffffffffffffffffffffffffffffffffff83168181048352604090922080546028929093069190910282811c851864ffffffffff16901b90911890555050565b6113bd6114ab565b63389a75e1600c52806000526020600c2080544211156113e557636f5e88186000526004601cfd5b60009055610794816114ed565b6113fa6114ab565b8060601b61141057637448fbae6000526004601cfd5b610794816114ed565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461097c576040517f2acd5d9d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107ef828260006115ee565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754331461097c576382b429006000526004601cfd5b6107ef828260016115ee565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927805473ffffffffffffffffffffffffffffffffffffffff9092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b638b78c6d8600c5233600052806020600c205416610794577fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543314610794576382b429006000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff8116610794576040517f32ae9d2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b638b78c6d8600c52826000526020600c20805483811783611610575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3505050505050565b60006020828403121561165957600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461079457600080fd5b6000806040838503121561169557600080fd5b82356116a081611660565b946020939093013593505050565b803564ffffffffff811681146116c357600080fd5b919050565b6000602082840312156116da57600080fd5b6116e3826116ae565b9392505050565b6000602082840312156116fc57600080fd5b81356116e381611660565b60008060006040848603121561171c57600080fd5b833567ffffffffffffffff8082111561173457600080fd5b818601915086601f83011261174857600080fd5b81358181111561175757600080fd5b8760208260051b850101111561176c57600080fd5b6020928301955093505084013561178281611660565b809150509250925092565b60006020828403121561179f57600080fd5b813560ff811681146116e357600080fd5b600080604083850312156117c357600080fd5b6117cc836116ae565b915060208301356117dc81611660565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761080c5761080c6117e7565b600082611863577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8181038181111561080c5761080c6117e7565b60006020828403121561188d57600080fd5b81516116e381611660565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060808201868352602060808185015281875180845260a086019150828901935060005b8181101561191e57845173ffffffffffffffffffffffffffffffffffffffff16835293830193918301916001016118ec565b505073ffffffffffffffffffffffffffffffffffffffff9690961660408501525050506060015292915050565b64ffffffffff828116828216039080821115611969576119696117e7565b5092915050565b64ffffffffff818116838216019080821115611969576119696117e756fea26469706673582212203796a22bebec0a1bc320ab1ba5eeff40cda41d1cdf8f2754d91b9f5f2156c89664736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f62ac0fcae17f9195280ced4de978313effe2daa000000000000000000000000ae010a324281de301d1bfb2611ef8873d88436220000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
-----Decoded View---------------
Arg [0] : chart_ (address): 0xF62aC0fCae17F9195280cEd4dE978313eFFE2Daa
Arg [1] : owner_ (address): 0xAE010A324281De301D1bFB2611EF8873D8843622
Arg [2] : router_ (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000f62ac0fcae17f9195280ced4de978313effe2daa
Arg [1] : 000000000000000000000000ae010a324281de301d1bfb2611ef8873d8843622
Arg [2] : 0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.