RatchetToken
0x20571610919e977b67050aa8d1583127a2e57759
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
4,495 bytes
ABI entries
36
12 read · 8 write
License
mit
Contract information
- Address
- 0x20571610919e977b67050aa8d1583127a2e57759
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x4bb9e0005E…75D16b8ac8
- Creation tx
- 0x609f63d7df…4386c178b4
Token
- Name
- Backstop.fun
- Symbol
- Backstop
- Decimals
- 18
- Holders
- 186
- Market
- View token page →
Read contract (12)
allowance(address, address) → uint256
balanceOf(address) → uint256
decimals() → uint8
estimateNetTransfer(uint256) → uint256, uint256
hook() → address
isTaxExempt(address) → bool
name() → string
owner() → address
poolManager() → address
symbol() → string
taxModule() → address
totalSupply() → uint256
Events (5)
ApprovalOwnershipTransferredTaxExemptUpdatedTransferTransferTax
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_hook",
"type": "address"
},
{
"internalType": "address",
"name": "_poolManager",
"type": "address"
},
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "allowance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientAllowance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientBalance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "approver",
"type": "address"
}
],
"name": "ERC20InvalidApprover",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "ERC20InvalidReceiver",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "ERC20InvalidSender",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "ERC20InvalidSpender",
"type": "error"
},
{
"inputs": [],
"name": "OnlyHook",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "Reentrancy",
"type": "error"
},
{
"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": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "TaxExemptUpdated",
"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"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "taxAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "taxBps",
"type": "uint256"
}
],
"name": "TransferTax",
"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": "value",
"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": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "estimateNetTransfer",
"outputs": [
{
"internalType": "uint256",
"name": "net",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tax",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "hook",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "hookBurn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "isTaxExempt",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"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": "poolManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "setTaxExempt",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "taxModule",
"outputs": [
{
"internalType": "contract IRatchetTaxModule",
"name": "",
"type": "address"
}
],
"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": "value",
"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": "value",
"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"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IRatchetTaxModule } from "./interfaces/IRatchetTaxModule.sol";
contract RatchetToken is ERC20, Ownable {
/// @notice The sole minting and burn authority (RatchetHook proxy).
address public immutable hook;
/// @notice Uniswap v4 PoolManager used by the official Hook.
address public immutable poolManager;
/// @notice Tax module, equal to the Hook in production.
IRatchetTaxModule public immutable taxModule;
/// @notice Addresses exempt from transfer tax; a transfer is exempt when either endpoint is exempt.
mapping(address => bool) public isTaxExempt;
uint256 private _taxLock;
/// @notice Emitted when an account's tax exemption changes.
/// @param account Updated account.
/// @param exempt Whether the account is exempt.
event TaxExemptUpdated(address indexed account, bool exempt);
/// @notice Emitted when transfer tax is burned and liquidated.
/// @param from Taxed sender.
/// @param to Transfer recipient.
/// @param taxAmount Burned tax amount.
/// @param taxBps Applied tax rate in basis points.
event TransferTax(address indexed from, address indexed to, uint256 taxAmount, uint256 taxBps);
/// @notice Reverts when the caller is not the Hook.
error OnlyHook();
/// @notice Reverts on reentry into tax liquidation.
error Reentrancy();
/// @notice Creates the token and binds its Hook, PoolManager and owner.
/// @param _hook Hook proxy address (mint/burn authority and tax module)
/// @param _poolManager Uniswap v4 PoolManager used by the official Hook
/// @param _owner Owner for `setTaxExempt` (typically the Hook deployer).
/// @param name_ Token name, configured at deployment.
/// @param symbol_ Token symbol, configured at deployment.
constructor(
address _hook,
address _poolManager,
address _owner,
string memory name_,
string memory symbol_
) ERC20(name_, symbol_) Ownable(_owner) {
hook = _hook;
poolManager = _poolManager;
taxModule = IRatchetTaxModule(_hook);
isTaxExempt[_hook] = true;
emit TaxExemptUpdated(_hook, true);
}
modifier onlyHook() {
if (msg.sender != hook) revert OnlyHook();
_;
}
/// @notice Allows the owner to add or remove a transfer-tax exemption.
/// @param account Account to update.
/// @param exempt Whether the account should be exempt.
function setTaxExempt(address account, bool exempt) external onlyOwner {
isTaxExempt[account] = exempt;
emit TaxExemptUpdated(account, exempt);
}
/// @notice Mints tokens to a recipient; callable only by the Hook.
/// @param to Token recipient.
/// @param amount Amount to mint.
function mint(address to, uint256 amount) external onlyHook {
_mint(to, amount);
}
/// @notice Burns tokens from an address; callable only by the Hook.
/// @param from Account whose tokens are burned.
/// @param amount Amount to burn.
function hookBurn(address from, uint256 amount) external onlyHook {
_burn(from, amount);
}
/// @notice Estimate net amount received and tax for a non-exempt transfer of `amount`.
/// @param amount Gross transfer amount.
/// @return net Estimated recipient amount.
/// @return tax Estimated tax amount.
function estimateNetTransfer(uint256 amount) external view returns (uint256 net, uint256 tax) {
if (amount == 0) return (0, 0);
uint256 bps = taxModule.transferTaxBps();
if (bps == 0) return (amount, 0);
tax = (amount * bps) / 10_000;
if (tax == 0) return (amount, 0);
if (!taxModule.canLiquidateTax(tax)) return (amount, 0);
return (amount - tax, tax);
}
function _update(address from, address to, uint256 value) internal override {
if (from == address(0) || to == address(0)) {
super._update(from, to, value);
return;
}
if (value == 0 || (from.code.length == 0 && to.code.length == 0) || isTaxExempt[from] || isTaxExempt[to]) {
super._update(from, to, value);
return;
}
uint256 bps = taxModule.transferTaxBps();
if (bps == 0) {
super._update(from, to, value);
return;
}
uint256 tax = (value * bps) / 10_000;
if (tax == 0) {
super._update(from, to, value);
return;
}
if (!taxModule.canLiquidateTax(tax)) {
super._update(from, to, value);
return;
}
if (_taxLock != 0) revert Reentrancy();
_taxLock = 1;
uint256 net = value - tax;
super._update(from, to, net);
super._update(from, address(0), tax);
taxModule.liquidateTax(tax);
emit TransferTax(from, to, tax, bps);
_taxLock = 0;
}
}
Chain explorer9509msChain node91ms