Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
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 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
interface IERC20Errors {
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
error ERC20InvalidSender(address sender);
error ERC20InvalidReceiver(address receiver);
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
error ERC20InvalidApprover(address approver);
error ERC20InvalidSpender(address spender);
}
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
function decimals() public view virtual returns (uint8) {
return 18;
}
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
interface IV3SwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 limitSqrtPrice;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
}
interface INonfungiblePositionManager {
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint160 sqrtPriceX96
) external payable returns (address pool);
struct MintParams {
address token0;
address token1;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function mint(MintParams calldata params) external payable returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
}
contract BRNx is ERC20, Ownable, ReentrancyGuard {
// CONSTANT VARIABLES
IV3SwapRouter public constant SWAP_ROUTER = IV3SwapRouter(0xA047e2AbF8263FcA7c368F43e2f960A06FD9949f);
INonfungiblePositionManager public constant NONFUNGIBLE_POSITION_MANAGER = INonfungiblePositionManager(0xd82Fe82244ad01AaD671576202F9b46b76fAdFE2);
address public constant TOKEN_PAIR_ADDRESS = 0xA04BC7140c26fc9BB1F36B1A604C7A5a88fb0E70; //SWPx
uint16 public constant BUY_FEES = 600; //6%
uint16 public constant BURN_SHARE = 400; //4%
uint16 public constant TREASURY_SHARE = 175; //1.75%
uint16 public constant CALLER_SHARE = 25; //0.25%
address private constant DEAD_ADDRESS = address(0xdead);
// OTHER VARIABLES
bool private swapping;
address public treasuryWallet;
address public devWallet;
address public algebraV3Pair;
uint256 public minimumBalanceBB;
uint40 public cooldownPeriodBB;
bool private limitsInEffect;
uint256 private maxW;
// MAPPINGS
mapping(address => bool) public isExcludedFromFees;
mapping(address => bool) public automatedMarketMakerPairs;
mapping(address => uint256) public lastBBCalledTime;
// DEVWALLET CHANGE TIMELOCK VARIABLES
struct DevWalletChange {
address newDevWallet;
uint256 effectiveTime;
bool pending;
}
DevWalletChange public pendingDevWalletChange;
uint256 public constant DEV_WALLET_TIMELOCK = 2 days;
event ChangeDevWallet(address indexed newDevWallet, address oldDevWallet);
// TREASURYWALLET CHANGE TIMELOCK VARIABLES
struct TreasuryWalletChange {
address newTreasuryWallet;
uint256 effectiveTime;
bool pending;
}
TreasuryWalletChange public pendingTreasuryWalletChange;
uint256 public constant TREASURY_WALLET_TIMELOCK = 1 days;
event ChangeTreasuryWallet(address indexed newTreasuryWallet, address oldTreasuryWallet);
// EVENTS
event ExcludeFromFees(address indexed account, bool isExcluded);
event ChangeAutomatedMarketMakerPairs(address indexed automatedMarketMakerPairs, bool isAutomatedMarketMakerPairs);
event ChangeMinimumBalanceBB(uint256 newMinimumBalanceBB, uint256 oldMinimumBalanceBB);
event ChangeCooldownPeriodBB(uint40 newCooldownPeriodBB, uint40 oldCooldownPeriodBB);
event BuyAndBurn(address indexed caller, uint256 amountToBurn, uint256 amountToTreasury, uint256 amountToCaller);
// MODIFIERS
modifier onlyDev() {
require(msg.sender == devWallet, "Only the dev can run this function");
_;
}
constructor(
)
ERC20("BurnX", "BRNx")
Ownable(msg.sender)
{
devWallet = msg.sender;
treasuryWallet = 0xF7f259F157481365ba2Eee1B692D29C876180fE2;
minimumBalanceBB = 1000 * 10**18; // 1000 BRNx
cooldownPeriodBB = 8 hours;
uint256 _totalSupply = 50_000_000 * 10**18; // 50M BRNx
maxW = _totalSupply * 2 / 100;
limitsInEffect = true;
// exclude from paying fees
isExcludedFromFees[address(this)] = true;
isExcludedFromFees[DEAD_ADDRESS] = true;
isExcludedFromFees[address(NONFUNGIBLE_POSITION_MANAGER)] = true;
isExcludedFromFees[treasuryWallet] = true;
_mint(address(this), _totalSupply);
}
// OWNER FUNCTIONS
function mintAndRenounce(uint160 _sqrtPriceX96, int24 _tickLower, int24 _tickUpper) public onlyOwner {
address token0;
address token1;
uint256 amount0Desired;
uint256 amount1Desired;
// create a new pair and initialize at a certain price
if (address(this) < TOKEN_PAIR_ADDRESS) {
algebraV3Pair = NONFUNGIBLE_POSITION_MANAGER.createAndInitializePoolIfNecessary(address(this), TOKEN_PAIR_ADDRESS, _sqrtPriceX96);
token0 = address(this);
token1 = TOKEN_PAIR_ADDRESS;
amount0Desired = balanceOf(address(this));
amount1Desired = 0;
}
else {
algebraV3Pair = NONFUNGIBLE_POSITION_MANAGER.createAndInitializePoolIfNecessary(TOKEN_PAIR_ADDRESS, address(this), _sqrtPriceX96);
token0 = TOKEN_PAIR_ADDRESS;
token1 = address(this);
amount0Desired = 0;
amount1Desired = balanceOf(address(this));
}
automatedMarketMakerPairs[algebraV3Pair] = true;
//addLP automatically
_approve(address(this), address(NONFUNGIBLE_POSITION_MANAGER), balanceOf(address(this)));
// build the params to mint
INonfungiblePositionManager.MintParams memory params;
params = INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
tickLower: _tickLower,
tickUpper: _tickUpper,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: 0,
amount1Min: 0,
recipient: devWallet,
deadline: block.timestamp
});
// mint the position
NONFUNGIBLE_POSITION_MANAGER.mint(params);
// renounce ownership
renounceOwnership();
}
// DEV FUNCTIONS -> can be run by the dev after renounce in order to manage the project even after the contract is renounced
function removeLimits() public onlyDev {
require(limitsInEffect, "Limits were removed already");
limitsInEffect = false;
// it's gonna call the buyAndBurn function first and send the caller's share to the treasury
_buyAndBurn(treasuryWallet);
}
function excludeFromFees(address _account, bool _excluded) public onlyDev nonReentrant {
require(!automatedMarketMakerPairs[_account], "Pool addresses cannot be excluded from fees!");
isExcludedFromFees[_account] = _excluded;
emit ExcludeFromFees(_account, _excluded);
}
function changeAutomatedMarketMakerPairs(address _automatedMarketMakerPairs, bool _isAutomatedMarketMakerPairs) public onlyDev nonReentrant {
require(_automatedMarketMakerPairs != address(0), "Automated Market Maker Pair must be a valid address");
require(_automatedMarketMakerPairs != algebraV3Pair, "Can't remove algebraV3Pair from the Automated Market Maker Pairs!");
automatedMarketMakerPairs[_automatedMarketMakerPairs] = _isAutomatedMarketMakerPairs;
emit ChangeAutomatedMarketMakerPairs(_automatedMarketMakerPairs, _isAutomatedMarketMakerPairs);
}
function changeMinimumBalanceBB(uint256 _newMinimumBalanceBB) public onlyDev nonReentrant {
require(_newMinimumBalanceBB > 1 * 10**18, "Cannot set the minimumBalanceBB to lower than 1 BRNx");
uint256 oldMinimumBalanceBB = minimumBalanceBB;
minimumBalanceBB = _newMinimumBalanceBB;
emit ChangeMinimumBalanceBB(_newMinimumBalanceBB, oldMinimumBalanceBB);
}
function changeCooldownPeriodBB(uint40 _newCooldownPeriodBB) public onlyDev nonReentrant {
require(
_newCooldownPeriodBB >= 1 minutes && _newCooldownPeriodBB <= 24 hours,
"cooldownPeriodBB must be between 1 minute and 24 hours"
);
uint40 oldCooldownPeriodBB = cooldownPeriodBB;
cooldownPeriodBB = _newCooldownPeriodBB;
emit ChangeCooldownPeriodBB(_newCooldownPeriodBB, oldCooldownPeriodBB);
}
// DEVWALLET CHANGE -> will be subject to a timelock as a measure of security
function initiateDevWalletChange(address _newDevWallet) public onlyDev nonReentrant {
require(_newDevWallet != address(0), "New Dev Wallet must be a valid address");
require(!pendingDevWalletChange.pending, "Dev Wallet change already pending");
pendingDevWalletChange = DevWalletChange({
newDevWallet: _newDevWallet,
effectiveTime: block.timestamp + DEV_WALLET_TIMELOCK,
pending: true
});
}
function executeDevWalletChange() public onlyDev nonReentrant {
require(pendingDevWalletChange.pending, "No Dev Wallet change pending");
require(block.timestamp >= pendingDevWalletChange.effectiveTime, "Timelock not expired");
address oldDevWallet = devWallet;
devWallet = pendingDevWalletChange.newDevWallet;
// Reset the pending change
pendingDevWalletChange.pending = false;
emit ChangeDevWallet(pendingDevWalletChange.newDevWallet, oldDevWallet);
}
function cancelDevWalletChange() public onlyDev nonReentrant {
require(pendingDevWalletChange.pending, "No Dev Wallet change pending");
pendingDevWalletChange.pending = false;
}
// TREASURYWALLET CHANGE -> will be subject to a timelock as a measure of security
function initiateTreasuryWalletChange(address _newTreasuryWallet) public onlyDev nonReentrant {
require(_newTreasuryWallet != address(0), "New Treasury Wallet must be a valid address");
require(!pendingTreasuryWalletChange.pending, "Treasury Wallet change already pending");
pendingTreasuryWalletChange = TreasuryWalletChange({
newTreasuryWallet: _newTreasuryWallet,
effectiveTime: block.timestamp + TREASURY_WALLET_TIMELOCK,
pending: true
});
}
function executeTreasuryWalletChange() public onlyDev nonReentrant {
require(pendingTreasuryWalletChange.pending, "No Treasury Wallet change pending");
require(block.timestamp >= pendingTreasuryWalletChange.effectiveTime, "Timelock not expired");
address oldTreasuryWallet = treasuryWallet;
treasuryWallet = pendingTreasuryWalletChange.newTreasuryWallet;
// Reset the pending change
pendingTreasuryWalletChange.pending = false;
emit ChangeTreasuryWallet(pendingTreasuryWalletChange.newTreasuryWallet, oldTreasuryWallet);
}
function cancelTreasuryWalletChange() public onlyDev nonReentrant {
require(pendingTreasuryWalletChange.pending, "No Treasury Wallet change pending");
pendingTreasuryWalletChange.pending = false;
}
// PRIVATE FUNCTIONS
function _tokenTransfer(address from, address to, uint256 amount) private {
if (
amount == 0 ||
isExcludedFromFees[from] ||
isExcludedFromFees[to] ||
(_isEOA(from) && _isEOA(to)) ||
swapping
) {
_transfer(from, to, amount);
return;
}
//when buy, charge tax
if (automatedMarketMakerPairs[from]) {
//apply an anti-snipe limit
if (limitsInEffect) {
require(amount + balanceOf(to) <= maxW, "Max wallet exceeded");
}
// calculate the fees and send them to this address
uint256 fees = amount * BUY_FEES / 10000;
amount -= fees;
_transfer(from, address(this), fees);
}
//when sell, do nothing and continue as normal
else if (automatedMarketMakerPairs[to]) {}
//can't be done
else {
revert("Can't buy, sell or add LP on another pool");
}
//in the end, it'll make the swap of the tokens as requested
_transfer(from, to, amount);
}
function _swapTokensForTokens(uint256 tokenAmount) private {
// generate the algebra pair path of token -> TOKEN_PAIR_ADDRESS
address tokenIn = address(this);
address tokenOut = address(TOKEN_PAIR_ADDRESS);
_approve(address(this), address(SWAP_ROUTER), tokenAmount);
// build the params to swap
IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
recipient: address(this),
amountIn: tokenAmount,
amountOutMinimum: 0,
limitSqrtPrice: 0
});
// make the swap
SWAP_ROUTER.exactInputSingle(params);
}
function _buyAndBurn(address caller) private {
// make sure there's something to swap and that it follows the rules
uint256 contractBalance = balanceOf(address(this));
require(contractBalance >= minimumBalanceBB, "Can only call the buyAndBurn function when BRNx balance of the contract is over the minimumBalanceBB");
require(
block.timestamp >= lastBBCalledTime[caller] + cooldownPeriodBB,
"Can only call the buyAndBurn function after the cooldownPeriodBB is over"
);
// update last called time
lastBBCalledTime[caller] = block.timestamp;
// swap the tokens to the TOKEN_PAIR_ADDRESS
swapping = true;
_swapTokensForTokens(contractBalance);
swapping = false;
// calculate amounts
IERC20 token = IERC20(TOKEN_PAIR_ADDRESS);
uint256 tokenContractBalance = token.balanceOf(address(this));
uint256 amountToBurn = tokenContractBalance * BURN_SHARE / BUY_FEES;
uint256 amountToTreasury = tokenContractBalance * TREASURY_SHARE / BUY_FEES;
uint256 amountToCaller = tokenContractBalance - amountToBurn - amountToTreasury; //remaining balance goes to the caller
// distribute amounts
token.transfer(DEAD_ADDRESS, amountToBurn); //burn
token.transfer(treasuryWallet, amountToTreasury); //treasury
token.transfer(caller, amountToCaller); //caller
emit BuyAndBurn(caller, amountToBurn, amountToTreasury, amountToCaller);
}
function _isEOA(address contractAddress) private view returns (bool) {
uint256 size;
assembly {
size := extcodesize(contractAddress)
}
return size == 0;
}
// PUBLIC FUNCTIONS
function transfer(address to, uint256 value) public virtual override returns (bool) {
address owner = _msgSender();
_tokenTransfer(owner, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_tokenTransfer(from, to, value);
return true;
}
// COMMUNITY FUNCTIONS
function buyAndBurn() external {
require(!limitsInEffect, "Can't call buyAndBurn while limits are in effect");
_buyAndBurn(msg.sender);
}
}