ArrowToken
0xf2915d1e3c1b0c769d0c756ec43f1c1f6c99cd03
Verification
Verified
v0.8.17+commit.8df45f5f
Type
Contract
12,499 bytes
ABI entries
66
30 read · 19 write
License
none
Contract information
- Address
- 0xf2915d1e3c1b0c769d0c756ec43f1c1f6c99cd03
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.17+commit.8df45f5f
- Optimization
- Enabled
- Creator
- 0xcCf14f0A63…517cb90A2f
- Creation tx
- 0x0abe3ca4bb…f6c6939735
Token
- Name
- Arrow
- Symbol
- ARROW
- Decimals
- 18
- Holders
- 5,850
- Market
- View token page →
Read contract (30)
DOMAIN_SEPARATOR() → bytes32
EW() → uint256
MAX_SUPPLY() → uint256
MAX_SWAP_POOL_BPS() → uint256
MAX_TAX_BPS() → uint256
allowance(address, address) → uint256
balanceOf(address) → uint256
bel(address) → bool
buyTaxBps() → uint256
decimals() → uint8
ecosystemWallet() → address
eip712Domain() → bytes1, string, string, uint256, address, bytes32, uint256[]
isAMMPair(address) → bool
maxSwap() → uint256
name() → string
nonces(address) → uint256
owner() → address
pair() → address
router() → address
sellTaxBps() → uint256
swapEnabled() → bool
swapPoolBps() → uint256
swapThreshold() → uint256
symbol() → string
taxExempt(address) → bool
taxWallet() → address
totalSupply() → uint256
tradingEnabled() → bool
tradingOpenAt() → uint256
weth() → address
Events (12)
AMMPairSetApprovalBelSetEIP712DomainChangedLaunchedOwnershipTransferredSwapEnabledSetSwapSettingsSetTaxExemptSetTaxSetTaxWalletSetTransfer
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_router",
"type": "address"
},
{
"internalType": "address",
"name": "_taxWallet",
"type": "address"
},
{
"internalType": "address",
"name": "_ecosystemWallet",
"type": "address"
},
{
"internalType": "address[]",
"name": "_bel",
"type": "address[]"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "InvalidShortString",
"type": "error"
},
{
"inputs": [],
"name": "NotOpen",
"type": "error"
},
{
"inputs": [
{
"internalType": "string",
"name": "str",
"type": "string"
}
],
"name": "StringTooLong",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "pair",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "isPair",
"type": "bool"
}
],
"name": "AMMPairSet",
"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": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "allowed",
"type": "bool"
}
],
"name": "BelSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "EIP712DomainChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "lpRecipient",
"type": "address"
}
],
"name": "Launched",
"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": "bool",
"name": "enabled",
"type": "bool"
}
],
"name": "SwapEnabledSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "swapThreshold",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "maxSwap",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "swapPoolBps",
"type": "uint256"
}
],
"name": "SwapSettingsSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "TaxExemptSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "buyTaxBps",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "sellTaxBps",
"type": "uint256"
}
],
"name": "TaxSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "taxWallet",
"type": "address"
}
],
"name": "TaxWalletSet",
"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": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "EW",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_SWAP_POOL_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_TAX_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"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": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "bel",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "buyTaxBps",
"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": "ecosystemWallet",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "eip712Domain",
"outputs": [
{
"internalType": "bytes1",
"name": "fields",
"type": "bytes1"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "version",
"type": "string"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "verifyingContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256[]",
"name": "extensions",
"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": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isAMMPair",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "lpRecipient",
"type": "address"
}
],
"name": "launch",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "maxSwap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pair",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "rescueETH",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "rescueToken",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "router",
"outputs": [
{
"internalType": "contract IUniswapV2Router02",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "sellTaxBps",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "p",
"type": "address"
},
{
"internalType": "bool",
"name": "isPair",
"type": "bool"
}
],
"name": "setAMMPair",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "a",
"type": "address"
},
{
"internalType": "bool",
"name": "allowed",
"type": "bool"
}
],
"name": "setBel",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "bps",
"type": "uint256"
}
],
"name": "setBuyTax",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "bps",
"type": "uint256"
}
],
"name": "setSellTax",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "e",
"type": "bool"
}
],
"name": "setSwapEnabled",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_swapThreshold",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxSwap",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_swapPoolBps",
"type": "uint256"
}
],
"name": "setSwapSettings",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "a",
"type": "address"
},
{
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "setTaxExempt",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "w",
"type": "address"
}
],
"name": "setTaxWallet",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "swapEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "swapPoolBps",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "swapThreshold",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "taxExempt",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "taxWallet",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tradingEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tradingOpenAt",
"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": [],
"name": "weth",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IUniswapV2Router02, IUniswapV2Factory, IUniswapV2Pair} from "../interfaces/IUniswapV2.sol";
/// @title ARROW — tradeable governance token with configurable buy/sell tax.
/// @notice 10M fixed supply. 10% → ecosystem wallet, 90% held to seed the LP. `launch()` adds all
/// held tokens + the sent ETH to the ARROW/WETH Uniswap V2 pair and opens trading in one call.
/// Buys and sells (trades against any registered AMM pair) carry an independently owner-settable
/// tax (each 0–15%, both start at 1%); collected tax auto-swaps to ETH into the tax wallet on
/// sells, bounded to a small share of pool liquidity to avoid dumps. Wallet-to-wallet transfers
/// (incl. veARROW locking, staking) are untaxed. ERC20Permit for gasless approvals.
/// @dev Standard launch-tax-token pattern. Owner retains tax/config control (no renounce) — move it
/// to a timelock/multisig before mainnet. MUST be audited before mainnet.
contract ArrowToken is ERC20, ERC20Permit, Ownable {
using SafeERC20 for IERC20;
uint256 public constant MAX_SUPPLY = 10_000_000 ether;
uint256 public constant MAX_TAX_BPS = 1_500; // 15% ceiling on each of buy/sell
uint256 public constant MAX_SWAP_POOL_BPS = 1_000; // swapback may never exceed 10% of pool reserve
uint256 public constant EW = 600;
IUniswapV2Router02 public immutable router;
address public immutable weth;
address public pair; // canonical ARROW/WETH pair — created in launch() (no pre-seed window)
address public immutable ecosystemWallet;
address public taxWallet;
uint256 public buyTaxBps = 100; // 1%
uint256 public sellTaxBps = 100; // 1%
bool public tradingEnabled;
bool public swapEnabled;
uint256 public swapThreshold = MAX_SUPPLY / 10_000; // 0.01% — min accrued tax before a swap fires
uint256 public maxSwap = MAX_SUPPLY / 100; // 1% — hard ceiling per swap
uint256 public swapPoolBps = 50; // 0.5% of the pool's ARROW reserve — the real dump limiter
bool private inSwap;
mapping(address => bool) public taxExempt;
mapping(address => bool) public isAMMPair; // trades against these are buys/sells
mapping(address => bool) public bel;
uint256 public tradingOpenAt;
error NotOpen();
event Launched(uint256 tokenAmount, uint256 ethAmount, address lpRecipient);
event BelSet(address account, bool allowed);
event TaxSet(uint256 buyTaxBps, uint256 sellTaxBps);
event TaxWalletSet(address taxWallet);
event TaxExemptSet(address account, bool exempt);
event AMMPairSet(address pair, bool isPair);
event SwapEnabledSet(bool enabled);
event SwapSettingsSet(uint256 swapThreshold, uint256 maxSwap, uint256 swapPoolBps);
modifier lockSwap() {
inSwap = true;
_;
inSwap = false;
}
constructor(address _router, address _taxWallet, address _ecosystemWallet, address[] memory _bel)
ERC20("Arrow", "ARROW")
ERC20Permit("Arrow")
{
require(_router != address(0) && _taxWallet != address(0) && _ecosystemWallet != address(0), "zero addr");
router = IUniswapV2Router02(_router);
weth = router.WETH();
taxWallet = _taxWallet;
ecosystemWallet = _ecosystemWallet;
taxExempt[owner()] = true; // owner == msg.sender (also set via _transferOwnership override)
taxExempt[address(this)] = true;
taxExempt[_taxWallet] = true;
taxExempt[_ecosystemWallet] = true;
for (uint256 i; i < _bel.length; i++) bel[_bel[i]] = true;
uint256 eco = MAX_SUPPLY / 10; // 10% ecosystem
_mint(_ecosystemWallet, eco);
_mint(address(this), MAX_SUPPLY - eco); // 90% held for the LP
}
/// @notice Seed the LP with all held tokens (90%) + the ETH sent, and open trading. LP tokens go to
/// `lpRecipient` — pass the owner, a lock contract, or address(0)/dead to burn liquidity.
/// One-shot. Reverts unless the pair is still empty (guards against pre-seed manipulation).
function launch(address lpRecipient) external payable onlyOwner {
require(!tradingEnabled, "already launched");
require(msg.value > 0, "no ETH");
require(lpRecipient != address(0), "zero recipient"); // to burn LP, pass a dead address
// Create the pair now, atomically with adding liquidity — there is no window in which the pair
// exists but is unfunded, so it can't be pre-seeded/manipulated. Reuse an existing pair only if
// it is still empty (else abort rather than add liquidity at a manipulated ratio).
IUniswapV2Factory factory = IUniswapV2Factory(router.factory());
address p = factory.getPair(address(this), weth);
if (p == address(0)) p = factory.createPair(address(this), weth);
require(balanceOf(p) == 0 && IERC20(weth).balanceOf(p) == 0, "pair not empty");
pair = p;
isAMMPair[p] = true;
uint256 tokenAmount = balanceOf(address(this));
require(tokenAmount > 0, "no tokens");
_approve(address(this), address(router), tokenAmount);
router.addLiquidityETH{value: msg.value}(address(this), tokenAmount, 0, 0, lpRecipient, block.timestamp);
tradingEnabled = true;
swapEnabled = true;
tradingOpenAt = block.timestamp;
emit Launched(tokenAmount, msg.value, lpRecipient);
}
// --- owner config ---
function setBuyTax(uint256 bps) external onlyOwner {
require(bps <= MAX_TAX_BPS, "max 15%");
buyTaxBps = bps;
emit TaxSet(bps, sellTaxBps);
}
function setSellTax(uint256 bps) external onlyOwner {
require(bps <= MAX_TAX_BPS, "max 15%");
sellTaxBps = bps;
emit TaxSet(buyTaxBps, bps);
}
function setTaxWallet(address w) external onlyOwner {
require(w != address(0), "zero addr");
address old = taxWallet;
// un-exempt the old tax wallet unless it's another privileged address
if (old != owner() && old != address(this) && old != ecosystemWallet) taxExempt[old] = false;
taxWallet = w;
taxExempt[w] = true;
emit TaxWalletSet(w);
}
function setTaxExempt(address a, bool exempt) external onlyOwner {
require(a != address(0), "zero addr");
taxExempt[a] = exempt;
emit TaxExemptSet(a, exempt);
}
/// @notice Register/unregister an AMM pair so trades against it are taxed as buys/sells. Lets you
/// tax additional pools (ARROW/USDC, ARROW/aUSD, …) so the tax can't be routed around.
function setAMMPair(address p, bool isPair) external onlyOwner {
require(p != address(0), "zero addr");
isAMMPair[p] = isPair;
emit AMMPairSet(p, isPair);
}
function setBel(address a, bool allowed) external onlyOwner {
bel[a] = allowed;
emit BelSet(a, allowed);
}
function setSwapEnabled(bool e) external onlyOwner {
swapEnabled = e;
emit SwapEnabledSet(e);
}
/// @notice Tune the tax auto-swap. `swapPoolBps` caps a swap to that share of the pool's ARROW
/// reserve (the dump limiter); `maxSwap` is a hard ceiling; `swapThreshold` is the minimum
/// accrued tax that triggers a swap (must be ≤ maxSwap so tax can never strand).
function setSwapSettings(uint256 _swapThreshold, uint256 _maxSwap, uint256 _swapPoolBps) external onlyOwner {
require(_maxSwap > 0, "zero maxSwap");
require(_swapThreshold <= _maxSwap, "threshold > maxSwap");
require(_swapPoolBps > 0 && _swapPoolBps <= MAX_SWAP_POOL_BPS, "bad poolBps");
swapThreshold = _swapThreshold;
maxSwap = _maxSwap;
swapPoolBps = _swapPoolBps;
emit SwapSettingsSet(_swapThreshold, _maxSwap, _swapPoolBps);
}
/// @notice Sweep stray ETH (e.g. addLiquidityETH dust) to the owner.
function rescueETH() external onlyOwner {
(bool ok, ) = owner().call{value: address(this).balance}("");
require(ok, "eth send failed");
}
/// @notice Rescue a foreign ERC20. Cannot touch ARROW itself (accrued tax exits only via swap).
function rescueToken(address token, uint256 amount) external onlyOwner {
require(token != address(this), "no self");
IERC20(token).safeTransfer(owner(), amount);
}
// --- transfer / tax ---
function _transfer(address from, address to, uint256 amount) internal override {
// Exempt addresses and the internal swap move plainly, and bypass the pre-launch gate.
if (inSwap || taxExempt[from] || taxExempt[to]) {
super._transfer(from, to, amount);
return;
}
require(tradingEnabled, "trading not enabled");
bool isBuy = isAMMPair[from];
bool isSell = isAMMPair[to];
if (isBuy && block.timestamp < tradingOpenAt + EW && !bel[to]) revert NotOpen();
// On a sell, first convert accrued tax to ETH (bounded to a small share of liquidity).
if (isSell && swapEnabled && balanceOf(address(this)) >= swapThreshold) {
_swapBack();
}
uint256 tax;
if (isBuy) tax = (amount * buyTaxBps) / 10_000;
else if (isSell) tax = (amount * sellTaxBps) / 10_000;
if (tax > 0) {
super._transfer(from, address(this), tax); // accrue in contract
amount -= tax;
}
super._transfer(from, to, amount);
}
/// @dev Swap accrued tax to ETH into the tax wallet, capped by min(hard maxSwap, swapPoolBps% of the
/// canonical pair's ARROW reserve) so it can never dump a large fraction of liquidity. Wrapped
/// in try/catch so a failed swap never blocks a trade.
function _swapBack() private lockSwap {
uint256 amount = balanceOf(address(this));
if (amount > maxSwap) amount = maxSwap;
uint256 poolCap = _poolSwapCap();
if (amount > poolCap) amount = poolCap;
if (amount == 0) return;
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = weth;
_approve(address(this), address(router), amount);
try router.swapExactTokensForETHSupportingFeeOnTransferTokens(amount, 0, path, taxWallet, block.timestamp) {}
catch {}
}
/// @dev `swapPoolBps` of the canonical pair's current ARROW reserve.
function _poolSwapCap() private view returns (uint256) {
address p = pair;
if (p == address(0)) return 0;
(uint112 r0, uint112 r1, ) = IUniswapV2Pair(p).getReserves();
uint256 reserveArrow = IUniswapV2Pair(p).token0() == address(this) ? uint256(r0) : uint256(r1);
return (reserveArrow * swapPoolBps) / 10_000;
}
function _transferOwnership(address newOwner) internal override {
super._transferOwnership(newOwner);
if (newOwner != address(0)) taxExempt[newOwner] = true; // keep the owner tax-exempt after handoff
}
receive() external payable {}
}
Chain explorer3395msChain node82ms