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
Creation tx
0x609f63d7df…4386c178b4

Token

Name
Backstop.fun
Symbol
Backstop
Decimals
18
Holders
186

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