编译器
0.8.21+commit.d9974bed
文件 1 的 10:ERC20.sol
pragma solidity ^0.8.4;
abstract contract ERC20 {
error TotalSupplyOverflow();
error AllowanceOverflow();
error AllowanceUnderflow();
error InsufficientBalance();
error InsufficientAllowance();
error InvalidPermit();
error PermitExpired();
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
function name() public view virtual returns (string memory);
function symbol() public view virtual returns (string memory);
function decimals() public view virtual returns (uint8) {
return 18;
}
function totalSupply() public view virtual returns (uint256 result) {
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
function balanceOf(address owner) public view virtual returns (uint256 result) {
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
function increaseAllowance(address spender, uint256 difference) public virtual returns (bool) {
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
let allowanceSlot := keccak256(0x0c, 0x34)
let allowanceBefore := sload(allowanceSlot)
let allowanceAfter := add(allowanceBefore, difference)
if lt(allowanceAfter, allowanceBefore) {
mstore(0x00, 0xf9067066)
revert(0x1c, 0x04)
}
sstore(allowanceSlot, allowanceAfter)
mstore(0x00, allowanceAfter)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
function decreaseAllowance(address spender, uint256 difference) public virtual returns (bool) {
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
let allowanceSlot := keccak256(0x0c, 0x34)
let allowanceBefore := sload(allowanceSlot)
if lt(allowanceBefore, difference) {
mstore(0x00, 0x8301ab38)
revert(0x1c, 0x04)
}
let allowanceAfter := sub(allowanceBefore, difference)
sstore(allowanceSlot, allowanceAfter)
mstore(0x00, allowanceAfter)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8)
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
assembly {
let from_ := shl(96, from)
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
if iszero(eq(allowance_, not(0))) {
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b)
revert(0x1c, 0x04)
}
sstore(allowanceSlot, sub(allowance_, amount))
}
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8)
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
function nonces(address owner) public view virtual returns (uint256 result) {
assembly {
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 domainSeparator = DOMAIN_SEPARATOR();
assembly {
let m := mload(0x40)
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc)
revert(0x1c, 0x04)
}
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
sstore(nonceSlot, add(nonceValue, 1))
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)
mstore(0, 0x1901)
mstore(0x20, domainSeparator)
mstore(0x40, keccak256(m, 0xc0))
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 iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef)
revert(0x1c, 0x04)
}
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m)
mstore(0x60, 0)
}
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
assembly {
result := mload(0x40)
}
bytes32 nameHash = keccak256(bytes(name()));
assembly {
let m := result
mstore(m, 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957)
revert(0x1c, 0x04)
}
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8)
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
assembly {
let from_ := shl(96, from)
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8)
revert(0x1c, 0x04)
}
sstore(fromBalanceSlot, sub(fromBalance, amount))
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
if iszero(eq(allowance_, not(0))) {
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b)
revert(0x1c, 0x04)
}
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
assembly {
let owner_ := shl(96, owner)
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
文件 2 的 10:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 3 的 10:IUniswapV2Factory.sol
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;
}
文件 4 的 10:IUniswapV2Router01.sol
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);
}
文件 5 的 10:IUniswapV2Router02.sol
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;
}
文件 6 的 10:LibMap.sol
pragma solidity ^0.8.4;
library LibMap {
struct Uint8Map {
mapping(uint256 => uint256) map;
}
struct Uint16Map {
mapping(uint256 => uint256) map;
}
struct Uint32Map {
mapping(uint256 => uint256) map;
}
struct Uint40Map {
mapping(uint256 => uint256) map;
}
struct Uint64Map {
mapping(uint256 => uint256) map;
}
struct Uint128Map {
mapping(uint256 => uint256) map;
}
function get(Uint8Map storage map, uint256 index) internal view returns (uint8 result) {
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(5, index))
result := byte(and(31, not(index)), sload(keccak256(0x00, 0x40)))
}
}
function set(Uint8Map storage map, uint256 index, uint8 value) internal {
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(5, index))
let s := keccak256(0x00, 0x40)
mstore(0x00, sload(s))
mstore8(and(31, not(index)), value)
sstore(s, mload(0x00))
}
}
function get(Uint16Map storage map, uint256 index) internal view returns (uint16 result) {
result = uint16(map.map[index >> 4] >> ((index & 15) << 4));
}
function set(Uint16Map storage map, uint256 index, uint16 value) internal {
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(4, index))
let s := keccak256(0x00, 0x40)
let o := shl(4, and(index, 15))
let v := sload(s)
let m := 0xffff
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
function get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) {
result = uint32(map.map[index >> 3] >> ((index & 7) << 5));
}
function set(Uint32Map storage map, uint256 index, uint32 value) internal {
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(3, index))
let s := keccak256(0x00, 0x40)
let o := shl(5, and(index, 7))
let v := sload(s)
let m := 0xffffffff
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
function get(Uint40Map storage map, uint256 index) internal view returns (uint40 result) {
unchecked {
result = uint40(map.map[index / 6] >> ((index % 6) * 40));
}
}
function set(Uint40Map storage map, uint256 index, uint40 value) internal {
assembly {
mstore(0x20, map.slot)
mstore(0x00, div(index, 6))
let s := keccak256(0x00, 0x40)
let o := mul(40, mod(index, 6))
let v := sload(s)
let m := 0xffffffffff
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
function get(Uint64Map storage map, uint256 index) internal view returns (uint64 result) {
result = uint64(map.map[index >> 2] >> ((index & 3) << 6));
}
function set(Uint64Map storage map, uint256 index, uint64 value) internal {
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(2, index))
let s := keccak256(0x00, 0x40)
let o := shl(6, and(index, 3))
let v := sload(s)
let m := 0xffffffffffffffff
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
function get(Uint128Map storage map, uint256 index) internal view returns (uint128 result) {
result = uint128(map.map[index >> 1] >> ((index & 1) << 7));
}
function set(Uint128Map storage map, uint256 index, uint128 value) internal {
assembly {
mstore(0x20, map.slot)
mstore(0x00, shr(1, index))
let s := keccak256(0x00, 0x40)
let o := shl(7, and(index, 1))
let v := sload(s)
let m := 0xffffffffffffffffffffffffffffffff
sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
}
}
}
文件 7 的 10:Nchart.sol
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;
uint216 endBlock;
}
IUniswapV2Router02 public immutable uniswapV2Router;
address public immutable uniswapV2Pair;
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;
uint256 public constant MAX_SWAP_AMOUNT = MAX_SUPPLY * 5 / 1_000;
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;
maxWallet = MAX_SUPPLY / 100;
swapTokensAtAmount = MAX_SUPPLY * 5 / 10_000;
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;
}
function removeLimits() external {
_requireIsOwner();
settings.limitsInEffect = false;
}
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);
}
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;
}
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) {
assembly {
let from_ := shl(96, from)
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
if iszero(eq(allowance_, not(0))) {
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b)
revert(0x1c, 0x04)
}
sstore(allowanceSlot, sub(allowance_, amount))
}
}
_transfer(from, to, amount);
return true;
}
function _transfer(address from, address to, uint256 amount) internal override {
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();
}
}
}
if (fromData.isBlacklisted) {
revert Nchart__SenderBlacklisted();
}
if (toData.isBlacklisted) {
revert Nchart__ReceiverBlacklisted();
}
if (amount == 0) {
return;
}
bool excludedFromFees = fromData.isExcludedFromFees || toData.isExcludedFromFees;
uint8 txType = 3;
if (fromData.isAutomatedMarketMaker) {
txType = 1;
} else if (toData.isAutomatedMarketMaker) {
txType = 2;
}
if (!_swapping) {
if (settingCache.limitsInEffect) {
if (txType == 1 && !toData.isExcludedFromMaxTransactionAmount) {
if (amount > maxTransactionAmount) {
revert Nchart__BuyAmountGreaterThanMax();
}
if (amount + balanceOf(to) > maxWallet) {
revert Nchart__MaxWalletAmountExceeded();
}
}
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) {
if (txType == 2) {
if (balanceOf(address(this)) >= swapTokensAtAmount) {
_swapping = true;
_swapBack();
_swapping = false;
}
}
}
}
if (txType < 3) {
bool takeFee = !_swapping;
if (excludedFromFees) {
takeFee = false;
}
uint256 fees = 0;
if (takeFee) {
Fees memory feeCache = feeAmounts;
if (txType == 2) {
if (feeCache.sell > 0) {
fees = amount * feeCache.sell / 100;
}
}
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;
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 {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router.WETH();
_approve(address(this), address(uniswapV2Router), tokenAmount);
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0,
path,
address(this),
block.timestamp
);
}
function _addLiquidity(uint256 tokenAmount, uint256 ethAmount) internal {
_approve(address(this), address(uniswapV2Router), tokenAmount);
uniswapV2Router.addLiquidityETH{value: ethAmount}(
address(this),
tokenAmount,
0,
0,
owner(),
block.timestamp
);
}
function _swapBack() internal {
uint256 contractBalance = balanceOf(address(this));
Fees memory feeCache = feeAmounts;
bool success;
if (contractBalance == 0) {
return;
}
uint256 maxAmount = swapTokensAtAmount * 20;
if (contractBalance > maxAmount) {
contractBalance = maxAmount;
}
uint256 liquidityAmount = contractBalance * feeCache.liquidity / 100;
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();
}
}
}
文件 8 的 10:Ownable.sol
pragma solidity ^0.8.4;
abstract contract Ownable {
error Unauthorized();
error NewOwnerIsZeroAddress();
error NoHandoverRequest();
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
event OwnershipHandoverRequested(address indexed pendingOwner);
event OwnershipHandoverCanceled(address indexed pendingOwner);
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
function _initializeOwner(address newOwner) internal virtual {
assembly {
newOwner := shr(96, shl(96, newOwner))
sstore(not(_OWNER_SLOT_NOT), newOwner)
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
function _setOwner(address newOwner) internal virtual {
assembly {
let ownerSlot := not(_OWNER_SLOT_NOT)
newOwner := shr(96, shl(96, newOwner))
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
sstore(ownerSlot, newOwner)
}
}
function _checkOwner() internal view virtual {
assembly {
if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
mstore(0x00, 0x82b42900)
revert(0x1c, 0x04)
}
}
}
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
function transferOwnership(address newOwner) public payable virtual onlyOwner {
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae)
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
function cancelOwnershipHandover() public payable virtual {
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818)
revert(0x1c, 0x04)
}
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
function owner() public view virtual returns (address result) {
assembly {
result := sload(not(_OWNER_SLOT_NOT))
}
}
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
assembly {
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
result := sload(keccak256(0x0c, 0x20))
}
}
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
文件 9 的 10:OwnableRoles.sol
pragma solidity ^0.8.4;
import {Ownable} from "./Ownable.sol";
abstract contract OwnableRoles is Ownable {
event RolesUpdated(address indexed user, uint256 indexed roles);
uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
function _setRoles(address user, uint256 roles) internal virtual {
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
sstore(keccak256(0x0c, 0x20), roles)
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
function _updateRoles(address user, uint256 roles, bool on) internal virtual {
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
let current := sload(roleSlot)
let updated := or(current, roles)
if iszero(on) { updated := xor(current, and(current, roles)) }
sstore(roleSlot, updated)
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
}
}
function _grantRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, true);
}
function _removeRoles(address user, uint256 roles) internal virtual {
_updateRoles(user, roles, false);
}
function _checkRoles(uint256 roles) internal view virtual {
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900)
revert(0x1c, 0x04)
}
}
}
function _checkOwnerOrRoles(uint256 roles) internal view virtual {
assembly {
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, 0x82b42900)
revert(0x1c, 0x04)
}
}
}
}
function _checkRolesOrOwner(uint256 roles) internal view virtual {
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, 0x82b42900)
revert(0x1c, 0x04)
}
}
}
}
function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
assembly {
for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
roles := or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
assembly {
ordinals := mload(0x40)
let ptr := add(ordinals, 0x20)
let o := 0
for { let t := roles } 1 {} {
mstore(ptr, o)
ptr := add(ptr, shl(5, and(t, 1)))
o := add(o, 1)
t := shr(o, roles)
if iszero(t) { break }
}
mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
mstore(0x40, ptr)
}
}
function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
_grantRoles(user, roles);
}
function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
_removeRoles(user, roles);
}
function renounceRoles(uint256 roles) public payable virtual {
_removeRoles(msg.sender, roles);
}
function rolesOf(address user) public view virtual returns (uint256 roles) {
assembly {
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
roles := sload(keccak256(0x0c, 0x20))
}
}
function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles != 0;
}
function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
return rolesOf(user) & roles == roles;
}
modifier onlyRoles(uint256 roles) virtual {
_checkRoles(roles);
_;
}
modifier onlyOwnerOrRoles(uint256 roles) virtual {
_checkOwnerOrRoles(roles);
_;
}
modifier onlyRolesOrOwner(uint256 roles) virtual {
_checkRolesOrOwner(roles);
_;
}
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;
}
文件 10 的 10:SubscriptionManagerV2.sol
pragma solidity 0.8.21;
import "@solady/auth/OwnableRoles.sol";
import "@solady/utils/LibMap.sol";
import {Nchart} from "./Nchart.sol";
import "./IUniswapV2Router02.sol";
contract SubscriptionManager is OwnableRoles {
struct StakingInfo {
address staker;
uint40 stakeLength;
uint40 stakingExpirationTimestamp;
uint256 stakedAmount;
}
struct StakingConfig {
uint40 stakeSubscriptionLength;
uint40 minStakingLength;
uint40 maxStakingLength;
uint256 maxStakingAmount;
uint256 step;
}
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;
mapping(address => StakingInfo) private staking;
StakingConfig public stakingConfig = StakingConfig({
stakeSubscriptionLength: 30 days * 6,
maxStakingAmount: 2000 * 1 ether,
minStakingLength: 30 days * 6,
maxStakingLength: 730 days,
step: 31565656565657
});
uint256 public subscriptionPrice = 0.015 ether;
uint256 public referralFeePriceReduction = 0.001 ether;
uint40 public subscriptionLength;
uint8 public burnPercent = 99;
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 Restaked(address indexed subscriberAddress, uint256 amount);
event Staked(address indexed subscriberAddress, uint40 stakeLength, uint256 chartPerAccount, uint40 stakingExpirationTimestamp);
event SubscriptionLengthUpdated(uint256 newLength, uint256 oldLength);
event SubscriptionPaid(address indexed subscriber, uint40 expirationTimestamp, uint256 price);
event SubscriptionPriceUpdated(uint256 newPrice, uint256 oldPrice);
event Unstaked(address indexed subscriberAddress, uint256 amount);
error SubscriptionManager__BurnPercentMustBeGreaterThan50();
error SubscriptionManager__BurnPercentMustBeLessThan100();
error SubscriptionManager__CannotReferSelf();
error SubscriptionManager__CannotReduceMoreThanSubscriptionPrice();
error SubscriptionManager__CannotRegisterAddressZero();
error SubscriptionManager__CannotRestakeForAccountsWithMoreThanOneMonthRemaining();
error SubscriptionManager__CannotRestakeForAccountsWithoutStake();
error SubscriptionManager__CannotStakeForAccountsWithCurrentSubscription();
error SubscriptionManager__CannotUnstakeForAccountsWithActiveStake();
error SubscriptionManager__CannotUnstakeForAccountsWithoutStake();
error SubscriptionManager__CanOnlyIncreaseExpiration();
error SubscriptionManager__ErrorRetrievingPriceFromDataFeed();
error SubscriptionManager__ErrorSendingKeeperFunds();
error SubscriptionManager__InvalidETHAmountProvided(uint256 msgValue, uint256 ethRequired);
error SubscriptionManager__MaxFiftyPercentReferralPercent();
error SubscriptionManager__MustBeStakerToRestake();
error SubscriptionManager__MustBeStakerToUnstake();
error SubscriptionManager__MustProvideAtLeastOneAddress();
error SubscriptionManager__MustProvideEqualLengthArrays();
error SubscriptionManager__OnlyOwner();
error SubscriptionManager__StakeLengthGreaterThanMaximumLength();
error SubscriptionManager__StakeLengthLessThanMinimumLength();
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();
}
function setSubscriptionLength(uint40 newSubscriptionLength) external {
_requireIsOwner();
uint40 oldSubscriptionLength = subscriptionLength;
subscriptionLength = newSubscriptionLength;
emit SubscriptionLengthUpdated(newSubscriptionLength, oldSubscriptionLength);
}
function setReferralPercent(uint8 newPercent) external {
_requireIsOwner();
if (newPercent > 50) {
revert SubscriptionManager__MaxFiftyPercentReferralPercent();
}
uint8 oldPercent = referralPercent;
referralPercent = newPercent;
emit ReferralPercentUpdated(newPercent, oldPercent);
}
function setReferralFeePriceReduction(uint256 newAmount) external {
_requireIsOwner();
if (newAmount >= subscriptionPrice) {
revert SubscriptionManager__CannotReduceMoreThanSubscriptionPrice();
}
uint256 oldReferralFeePriceReduction = referralFeePriceReduction;
referralFeePriceReduction = newAmount;
emit ReferralFeePriceReductionUpdated(newAmount, oldReferralFeePriceReduction);
}
function setSubscriptionPrice(uint256 newSubscriptionPrice) external {
_requireIsOwner();
uint256 oldSubscriptionPrice = subscriptionPrice;
subscriptionPrice = newSubscriptionPrice;
emit SubscriptionPriceUpdated(newSubscriptionPrice, oldSubscriptionPrice);
}
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);
}
function updateStakingConfig(uint40 stakeSubscriptionLength_, uint40 minStakingLength_, uint40 maxStakingLength_, uint256 maxStakingAmount_, uint256 step_) external {
_requireIsOwner();
stakingConfig = StakingConfig({
stakeSubscriptionLength: stakeSubscriptionLength_,
minStakingLength: minStakingLength_,
maxStakingLength: maxStakingLength_,
maxStakingAmount: maxStakingAmount_,
step: step_
});
}
function setExpirationTimestamp(uint40 newExpiration, address user) external {
_requireIsOwner();
if (user == address(0)) {
revert SubscriptionManager__CannotRegisterAddressZero();
}
LibMap.set(expiration, uint256(uint160(user)), newExpiration);
}
function bulkSetExpirationTimestamp(uint40[] calldata newExpiration, address[] calldata user) external {
_requireIsOwner();
if (newExpiration.length != user.length) {
revert SubscriptionManager__MustProvideEqualLengthArrays();
}
for (uint256 i = 0; i < newExpiration.length;) {
_requireValidAddress(user[i]);
LibMap.set(expiration, uint256(uint160(user[i])), newExpiration[i]);
unchecked {
++i;
}
}
}
function grantKeeperRole(address newKeeper) external {
grantRoles(newKeeper, KEEPER_ROLE);
}
function revokeKeeperRole(address toRevoke) external {
revokeRoles(toRevoke, KEEPER_ROLE);
}
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 (referrer != address(0)) {
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 = _increaseExpiration(addr, subLength, maxExpiration);
emit SubscriptionPaid(addr, addrExpiration, subPrice);
unchecked {
++i;
}
}
}
if (referralAmount > 0) {
payable(referrer).transfer(referralAmount);
emit ReferralPaid(referrer, referralAmount);
}
}
function stake(uint40 stakeLength, address[] calldata addresses) external {
uint256 numSubs = addresses.length;
if (numSubs == 0) {
revert SubscriptionManager__MustProvideAtLeastOneAddress();
}
StakingConfig memory config = stakingConfig;
if (stakeLength > config.maxStakingLength) {
revert SubscriptionManager__StakeLengthGreaterThanMaximumLength();
}
if (stakeLength < config.minStakingLength) {
revert SubscriptionManager__StakeLengthLessThanMinimumLength();
}
uint256 chartPerAccount = config.maxStakingAmount - ((stakeLength - config.minStakingLength) * config.step);
uint256 chartRequired = chartPerAccount * numSubs;
uint40 subscriptionExpirationTimestamp = uint40(block.timestamp + config.stakeSubscriptionLength);
uint40 stakingExpirationTimestamp = uint40(block.timestamp + stakeLength);
chart.transferFrom(msg.sender, address(this), chartRequired);
for(uint256 i = 0; i < numSubs;) {
address subscriberAddress = addresses[i];
_requireValidAddress(subscriberAddress);
if (block.timestamp <= LibMap.get(expiration, uint256(uint160(subscriberAddress)))) {
revert SubscriptionManager__CannotStakeForAccountsWithCurrentSubscription();
}
LibMap.set(expiration, uint256(uint160(subscriberAddress)), subscriptionExpirationTimestamp);
staking[subscriberAddress] = StakingInfo(msg.sender, stakeLength, stakingExpirationTimestamp, chartPerAccount);
emit Staked(subscriberAddress, stakeLength, chartPerAccount, stakingExpirationTimestamp);
unchecked {
++i;
}
}
}
function unstake(address[] calldata addresses) external {
uint256 numSubs = addresses.length;
if (numSubs == 0) {
revert SubscriptionManager__MustProvideAtLeastOneAddress();
}
uint256 amountToRefund = 0;
for(uint256 i = 0; i < numSubs;) {
address subscriberAddress = addresses[i];
_requireValidAddress(subscriberAddress);
StakingInfo memory info = staking[subscriberAddress];
if (info.stakedAmount == 0) {
revert SubscriptionManager__CannotUnstakeForAccountsWithoutStake();
}
if (block.timestamp <= info.stakingExpirationTimestamp) {
revert SubscriptionManager__CannotUnstakeForAccountsWithActiveStake();
}
if (msg.sender != info.staker) {
revert SubscriptionManager__MustBeStakerToUnstake();
}
amountToRefund += info.stakedAmount;
delete staking[subscriberAddress];
emit Unstaked(subscriberAddress, info.stakedAmount);
unchecked {
++i;
}
}
chart.transfer(msg.sender, amountToRefund);
}
function restake(address[] calldata addresses) external {
uint256 numSubs = addresses.length;
if (numSubs == 0) {
revert SubscriptionManager__MustProvideAtLeastOneAddress();
}
for(uint256 i = 0; i < numSubs;) {
address subscriberAddress = addresses[i];
_requireValidAddress(subscriberAddress);
StakingInfo memory info = staking[subscriberAddress];
if (info.stakedAmount == 0) {
revert SubscriptionManager__CannotRestakeForAccountsWithoutStake();
}
if (block.timestamp < info.stakingExpirationTimestamp - 30 days) {
revert SubscriptionManager__CannotRestakeForAccountsWithMoreThanOneMonthRemaining();
}
if (info.staker != msg.sender) {
revert SubscriptionManager__MustBeStakerToRestake();
}
staking[subscriberAddress].stakingExpirationTimestamp += info.stakeLength;
StakingConfig memory config = stakingConfig;
_increaseExpiration(subscriberAddress, config.stakeSubscriptionLength, type(uint40).max - config.stakeSubscriptionLength);
emit Restaked(subscriberAddress, info.stakedAmount);
unchecked {
++i;
}
}
}
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
);
(bool success,) = payable(msg.sender).call{value: amountToSend}("");
if (!success) {
revert SubscriptionManager__ErrorSendingKeeperFunds();
}
}
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)));
}
function getStakingExpiration(address user) external view returns (uint40) {
return staking[user].stakingExpirationTimestamp;
}
function chartRequiredForStake(uint40 stakeLength) external view returns (uint256) {
StakingConfig memory config = stakingConfig;
if (stakeLength > config.maxStakingLength) {
revert SubscriptionManager__StakeLengthGreaterThanMaximumLength();
}
if (stakeLength < config.minStakingLength) {
revert SubscriptionManager__StakeLengthLessThanMinimumLength();
}
return config.maxStakingAmount - ((stakeLength - config.minStakingLength) * config.step);
}
function canRestake(address addr) external view returns (bool) {
StakingInfo memory info = staking[addr];
return info.stakedAmount > 0 && block.timestamp < info.stakingExpirationTimestamp - 30 days;
}
function _increaseExpiration(address addr, uint40 subLength, uint40 maxExpiration) internal returns (uint40) {
uint256 uintAddr = uint256(uint160(addr));
uint40 addrExpiration = LibMap.get(expiration, uintAddr);
uint40 timestamp = uint40(block.timestamp);
if (addrExpiration <= timestamp) {
if (timestamp > maxExpiration) {
addrExpiration = type(uint40).max;
} else {
addrExpiration = timestamp + subLength;
}
} else if (addrExpiration < maxExpiration) {
unchecked {
addrExpiration += subLength;
}
} else {
addrExpiration = type(uint40).max;
}
LibMap.set(expiration, uintAddr, addrExpiration);
return addrExpiration;
}
function _requireIsOwner() internal view {
if (msg.sender != owner()) {
revert SubscriptionManager__OnlyOwner();
}
}
function _requireValidAddress(address addr) internal pure {
if (addr == address(0)) {
revert SubscriptionManager__CannotRegisterAddressZero();
}
}
}
{
"compilationTarget": {
"src/SubscriptionManagerV2.sol": "SubscriptionManager"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 500000
},
"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/"
]
}
[{"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__CannotRestakeForAccountsWithMoreThanOneMonthRemaining","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotRestakeForAccountsWithoutStake","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotStakeForAccountsWithCurrentSubscription","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotUnstakeForAccountsWithActiveStake","type":"error"},{"inputs":[],"name":"SubscriptionManager__CannotUnstakeForAccountsWithoutStake","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__MustBeStakerToRestake","type":"error"},{"inputs":[],"name":"SubscriptionManager__MustBeStakerToUnstake","type":"error"},{"inputs":[],"name":"SubscriptionManager__MustProvideAtLeastOneAddress","type":"error"},{"inputs":[],"name":"SubscriptionManager__MustProvideEqualLengthArrays","type":"error"},{"inputs":[],"name":"SubscriptionManager__OnlyOwner","type":"error"},{"inputs":[],"name":"SubscriptionManager__StakeLengthGreaterThanMaximumLength","type":"error"},{"inputs":[],"name":"SubscriptionManager__StakeLengthLessThanMinimumLength","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":"subscriberAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Restaked","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":true,"internalType":"address","name":"subscriberAddress","type":"address"},{"indexed":false,"internalType":"uint40","name":"stakeLength","type":"uint40"},{"indexed":false,"internalType":"uint256","name":"chartPerAccount","type":"uint256"},{"indexed":false,"internalType":"uint40","name":"stakingExpirationTimestamp","type":"uint40"}],"name":"Staked","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"subscriberAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"KEEPER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint40[]","name":"newExpiration","type":"uint40[]"},{"internalType":"address[]","name":"user","type":"address[]"}],"name":"bulkSetExpirationTimestamp","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"addr","type":"address"}],"name":"canRestake","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":"uint40","name":"stakeLength","type":"uint40"}],"name":"chartRequiredForStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"user","type":"address"}],"name":"getStakingExpiration","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":"addresses","type":"address[]"}],"name":"restake","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"uint40","name":"stakeLength","type":"uint40"},{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingConfig","outputs":[{"internalType":"uint40","name":"stakeSubscriptionLength","type":"uint40"},{"internalType":"uint40","name":"minStakingLength","type":"uint40"},{"internalType":"uint40","name":"maxStakingLength","type":"uint40"},{"internalType":"uint256","name":"maxStakingAmount","type":"uint256"},{"internalType":"uint256","name":"step","type":"uint256"}],"stateMutability":"view","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"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"stakeSubscriptionLength_","type":"uint40"},{"internalType":"uint40","name":"minStakingLength_","type":"uint40"},{"internalType":"uint40","name":"maxStakingLength_","type":"uint40"},{"internalType":"uint256","name":"maxStakingAmount_","type":"uint256"},{"internalType":"uint256","name":"step_","type":"uint256"}],"name":"updateStakingConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]