// SPDX-License-Identifier: MIT
pragma solidity =0.8.9;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/token/ERC20/ERC20.sol";
import "https://github.com/Uniswap/v2-core/blob/master/contracts/interfaces/IUniswapV2Factory.sol";
import "https://github.com/Uniswap/v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol";
library Liquidity {
address public constant FACTORY =
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address public constant ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant DEAD_ADDRESS =
0x000000000000000000000000000000000000dEaD;
uint public constant MIN_PERCENT = 100;
uint public constant MAX_PRECENT = 10000;
function swap(
address _tokenIn,
address _tokenOut,
uint _amountIn,
uint _slippage,
address _to
) internal returns (uint) {
approveIERC20(_tokenIn, ROUTER, _amountIn);
address[] memory path;
if (_tokenIn == DAI || _tokenOut == DAI) {
path = new address[](2);
path[0] = _tokenIn;
path[1] = _tokenOut;
} else {
path = new address[](4);
path[0] = _tokenIn;
path[1] = DAI;
path[2] = WETH;
path[3] = _tokenOut;
}
uint _amountOutMin = (IUniswapV2Router02(ROUTER).getAmountsOut(
_amountIn,
path
)[path.length - 1] * (MAX_PRECENT - _slippage)) / MAX_PRECENT;
uint balanceBefore = balanceOfIERC20(_tokenOut, _to);
IUniswapV2Router02(ROUTER)
.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
_amountOutMin,
path,
_to,
block.timestamp
);
uint balanceAfter = balanceOfIERC20(_tokenOut, _to);
return balanceAfter - balanceBefore;
}
function getPair(address _tokenA, address _tokenB)
internal
view
returns (address)
{
return IUniswapV2Factory(FACTORY).getPair(_tokenA, _tokenB);
}
function approveIERC20(
address _token,
address _to,
uint _amount
) internal returns (bool) {
return IERC20(_token).approve(_to, _amount);
}
function balanceOfIERC20(address _token, address _user)
internal
view
returns (uint)
{
return IERC20(_token).balanceOf(_user);
}
}
// SPDX-License-Identifier: MIT
pragma solidity =0.8.9;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.8/contracts/token/ERC20/ERC20.sol";
import "./Liquidity.sol";
contract ModulusDomainService is ERC20, Ownable {
uint private _feesOnContract;
uint private _slippage;
address public cult;
uint public cultAllocation;
address public development;
uint public developmentAllocation;
address public marketing;
uint public marketingAllocation;
uint public buyTax = 400;
uint public sellTax = 800;
uint public perWalletHoldingPercent;
error InvalidAddress();
error InvalidAllocation();
error InvalidPercent();
error InvalidTax();
error TransferLimitExceeded();
event AllocationsUpdated(
uint cultAllocation,
uint developmentAllocation,
uint marketingAllocation
);
event BuyTax(uint buyTax);
event SellTax(uint sellTax);
event Cult(address cult);
event Development(address development);
event Marketing(address marketing);
event PerWalletHoldingPercent(uint perWalletHoldingPercent);
constructor(
string memory _name,
string memory _symbol,
uint _supply,
address _cult,
uint _cultAllocation,
address _development,
uint _developmentAllocation,
address _marketing,
uint _marketingAllocation,
uint _perWalletHoldingPercent
) ERC20(_name, _symbol) {
if (
_cult == address(0) ||
_development == address(0) ||
_marketing == address(0)
) revert InvalidAddress();
if (
_cultAllocation == 0 ||
_developmentAllocation == 0 ||
_marketingAllocation == 0 ||
_cultAllocation + _developmentAllocation + _marketingAllocation !=
Liquidity.MAX_PRECENT
) revert InvalidAllocation();
if (
_perWalletHoldingPercent == 0 ||
_perWalletHoldingPercent > Liquidity.MAX_PRECENT
) {
revert InvalidPercent();
}
_mint(msg.sender, _supply);
cult = _cult;
cultAllocation = _cultAllocation;
marketing = _marketing;
marketingAllocation = _marketingAllocation;
development = _development;
developmentAllocation = _developmentAllocation;
_slippage = sellTax + Liquidity.MIN_PERCENT;
perWalletHoldingPercent = _perWalletHoldingPercent;
}
function isValidTransfer(uint _amount, address _to)
private
view
returns (bool)
{
uint validTokenTransfer = ((totalSupply() * perWalletHoldingPercent) /
Liquidity.MAX_PRECENT) - balanceOf(_to);
return _amount <= validTokenTransfer;
}
function _transfer(
address from,
address to,
uint amount
) internal virtual override {
address pair = Liquidity.getPair(address(this), Liquidity.DAI);
if (
to != pair &&
to != address(this) &&
to != owner() &&
to != development &&
to != marketing &&
to != Liquidity.DEAD_ADDRESS
)
if (!isValidTransfer(amount, to)) revert TransferLimitExceeded();
if (
(pair == to && from != address(this) && from != owner()) ||
(pair == from && to != address(this) && to != owner())
) {
uint tax = from == pair ? buyTax : sellTax;
uint feeAmount = (amount * tax) / Liquidity.MAX_PRECENT;
super._transfer(from, address(this), feeAmount);
_feesOnContract += feeAmount;
if (from != pair) {
uint _totalAllocation = cultAllocation +
developmentAllocation +
marketingAllocation;
uint _cultAllocation = (_feesOnContract * cultAllocation) /
_totalAllocation;
uint _developmentAllocation = (_feesOnContract *
developmentAllocation) / _totalAllocation;
uint _marketingAllocation = _feesOnContract -
(_cultAllocation + _developmentAllocation);
_buyBack(cult, Liquidity.DEAD_ADDRESS, _cultAllocation);
_buyBack(Liquidity.DAI, development, _developmentAllocation);
_buyBack(Liquidity.DAI, marketing, _marketingAllocation);
_feesOnContract = 0;
} else {
if (!isValidTransfer(amount, to))
revert TransferLimitExceeded();
}
return super._transfer(from, to, amount - feeAmount);
} else return super._transfer(from, to, amount);
}
function _buyBack(
address _tokenOut,
address _to,
uint _amountIn
) private {
if (_amountIn > 0) {
Liquidity.swap(address(this), _tokenOut, _amountIn, _slippage, _to);
}
}
function updateBuyTax(uint _tax) external onlyOwner {
if (_tax == 0 || _tax > Liquidity.MAX_PRECENT) revert InvalidTax();
buyTax = _tax;
emit BuyTax(_tax);
}
function updateSellTax(uint _tax) external onlyOwner {
if (_tax == 0 || _tax > Liquidity.MAX_PRECENT) revert InvalidTax();
sellTax = _tax;
_slippage = _tax + Liquidity.MIN_PERCENT;
emit SellTax(_tax);
}
function updateCult(address _token) external onlyOwner {
if (_token == address(0)) revert InvalidAddress();
cult = _token;
emit Cult(_token);
}
function updateDevelopment(address _account) external onlyOwner {
if (_account == address(0)) revert InvalidAddress();
development = _account;
emit Development(_account);
}
function updateMarketing(address _account) external onlyOwner {
if (_account == address(0)) revert InvalidAddress();
marketing = _account;
emit Marketing(_account);
}
function updateAllocations(
uint _cultAllocation,
uint _developmentAllocation,
uint _marketingAllocation
) external onlyOwner {
if (
_cultAllocation == 0 ||
_developmentAllocation == 0 ||
_marketingAllocation == 0 ||
_cultAllocation + _developmentAllocation + _marketingAllocation !=
Liquidity.MAX_PRECENT
) revert InvalidAllocation();
cultAllocation = _cultAllocation;
developmentAllocation = _developmentAllocation;
marketingAllocation = _marketingAllocation;
emit AllocationsUpdated(
cultAllocation,
developmentAllocation,
marketingAllocation
);
}
function updatePerWalletHoldingPercent(uint _perWalletHoldingPercent)
external
onlyOwner
{
if (
_perWalletHoldingPercent == 0 ||
_perWalletHoldingPercent > Liquidity.MAX_PRECENT
) revert InvalidPercent();
perWalletHoldingPercent = _perWalletHoldingPercent;
emit PerWalletHoldingPercent(perWalletHoldingPercent);
}
}
{
"compilationTarget": {
"Mods.sol": "ModulusDomainService"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"address","name":"_cult","type":"address"},{"internalType":"uint256","name":"_cultAllocation","type":"uint256"},{"internalType":"address","name":"_development","type":"address"},{"internalType":"uint256","name":"_developmentAllocation","type":"uint256"},{"internalType":"address","name":"_marketing","type":"address"},{"internalType":"uint256","name":"_marketingAllocation","type":"uint256"},{"internalType":"uint256","name":"_perWalletHoldingPercent","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAllocation","type":"error"},{"inputs":[],"name":"InvalidPercent","type":"error"},{"inputs":[],"name":"InvalidTax","type":"error"},{"inputs":[],"name":"TransferLimitExceeded","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cultAllocation","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"developmentAllocation","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"marketingAllocation","type":"uint256"}],"name":"AllocationsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"buyTax","type":"uint256"}],"name":"BuyTax","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"cult","type":"address"}],"name":"Cult","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"development","type":"address"}],"name":"Development","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"marketing","type":"address"}],"name":"Marketing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perWalletHoldingPercent","type":"uint256"}],"name":"PerWalletHoldingPercent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sellTax","type":"uint256"}],"name":"SellTax","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cult","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cultAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"development","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"developmentAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"marketing","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketingAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perWalletHoldingPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sellTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cultAllocation","type":"uint256"},{"internalType":"uint256","name":"_developmentAllocation","type":"uint256"},{"internalType":"uint256","name":"_marketingAllocation","type":"uint256"}],"name":"updateAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tax","type":"uint256"}],"name":"updateBuyTax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"updateCult","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"updateDevelopment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"updateMarketing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_perWalletHoldingPercent","type":"uint256"}],"name":"updatePerWalletHoldingPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tax","type":"uint256"}],"name":"updateSellTax","outputs":[],"stateMutability":"nonpayable","type":"function"}]