LootProtocolToken
0xcf703eaed1608a2f8a89228f1192f6f9cb4db99c
Showing partial data — Chain explorer is unavailable right now.
Verification
Verified
v0.8.25+commit.b61c2a91
Type
Contract
5,515 bytes
ABI entries
33
13 read · 11 write
License
none
Contract information
- Address
- 0xcf703eaed1608a2f8a89228f1192f6f9cb4db99c
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.25+commit.b61c2a91
- Optimization
- Enabled
- Creator
- —
- Creation tx
- —
Token
- Name
- RobinLoot
- Symbol
- LOOT
- Decimals
- 18
- Holders
- 27
- Market
- View token page →
Read contract (13)
CAP() → uint256
PREMINE() → uint256
allowance(address, address) → uint256
balanceOf(address) → uint256
cap() → uint256
decimals() → uint8
lpNFTId() → uint256
minter() → address
name() → string
owner() → address
symbol() → string
totalSupply() → uint256
uniswapV3Pool() → address
Events (7)
ApprovalExemptSetIncreasedLimitsMinterSetOwnershipTransferredStartedTradingTransfer
ABI
[
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "maxWallet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxTx",
"type": "uint256"
}
],
"internalType": "struct LootProtocolToken.Initializer",
"name": "params",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "ownerAddress",
"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": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "ExemptSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "maxWallet",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "maxTx",
"type": "uint256"
}
],
"name": "IncreasedLimits",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "minter",
"type": "address"
}
],
"name": "MinterSet",
"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": "address",
"name": "pair",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "lpId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "pmgr",
"type": "address"
}
],
"name": "StartedTrading",
"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": "CAP",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PREMINE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "o",
"type": "address"
},
{
"internalType": "address",
"name": "s",
"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": "a",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "cap",
"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": "_maxWallet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxTx",
"type": "uint256"
}
],
"name": "increaseLimits",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "lpNFTId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"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": "minter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"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": [
{
"internalType": "address",
"name": "erc20",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "recoverERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "recoverEth",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "setExempt",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "m",
"type": "address"
}
],
"name": "setMinter",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pmgr",
"type": "address"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"components": [
{
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"internalType": "address",
"name": "token1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint256",
"name": "amount0Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Desired",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount0Min",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1Min",
"type": "uint256"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"internalType": "struct INonfungiblePositionManager.MintParams",
"name": "params",
"type": "tuple"
}
],
"name": "startTrading",
"outputs": [],
"stateMutability": "nonpayable",
"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": [],
"name": "uniswapV3Pool",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
//
// =========================================================
// ____ __ _ __ __
// / __ \____ / /_ (_)___ / / ____ ____ / /_
// / /_/ / __ \/ __ \/ / __ \ / / / __ \/ __ \/ __/
// / _, _/ /_/ / /_/ / / / / / / /___/ /_/ / /_/ / /_
// /_/ |_|\____/_.___/_/_/ /_/ /_____/\____/\____/\__/
// =========================================================
//
pragma solidity ^0.8.25;
import {INonfungiblePositionManager} from "./RobinLootInterfaces.sol";
/**
* @title LootProtocolToken ($LOOT)
* @notice The RobinLoot protocol token. A 1,000,000 premine is minted to this
* contract at genesis and seeded one-sided into a Uniswap V3 LP by
* `startTrading`; the LP NFT is minted to the owner wallet.
*
* Supply is mineable up to `cap` (21,000,000): `mint` is callable only by
* the `minter` contract, is bounded by the cap, and makes no external
* call. Optional anti-snipe wallet/tx limits (owner can raise/disable).
*/
contract LootProtocolToken {
mapping(address => uint256) balances_;
mapping(address => mapping(address => uint256)) allowances_;
mapping(address => bool) isExempted_;
address owner_;
address spawner_;
address minter_; // the only address that may mint
bool tradingEnabled_;
uint256 tradingEnabledTs_;
uint8 decimals_;
uint256 supply_; // circulating supply (grows as mining mints)
uint256 cap_; // hard ceiling: premine + all mining
string name_;
string symbol_;
uint256 maxWallet_;
uint256 maxTx_;
bool walletLtd_;
INonfungiblePositionManager positionManager_;
address uniswapV3Pool_;
uint256 lpNFTId_;
bool isEntered_;
uint256 public constant PREMINE = 1_000_000 * 1e18;
uint256 public constant CAP = 21_000_000 * 1e18;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed ownerAddress, address indexed spender, uint256 value);
event StartedTrading(address pair, uint256 lpId, address pmgr);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event IncreasedLimits(uint256 maxWallet, uint256 maxTx);
event ExemptSet(address indexed account, bool exempt);
event MinterSet(address indexed minter);
struct Initializer {
address owner;
uint256 maxWallet; // bps of supply (10000 = unlimited)
uint256 maxTx; // bps of supply (10000 = unlimited)
}
// reentrancy lock
modifier lockTheEntry {
require(!isEntered_, "re");
isEntered_ = true;
_;
isEntered_ = false;
}
constructor(Initializer memory params) {
require(params.owner != address(0), "z");
spawner_ = msg.sender;
owner_ = params.owner;
decimals_ = 18;
name_ = "RobinLoot";
symbol_ = "LOOT";
supply_ = PREMINE;
cap_ = CAP;
walletLtd_ = true;
maxWallet_ = (supply_ * params.maxWallet) / 10000;
maxTx_ = (supply_ * params.maxTx) / 10000;
if (params.maxWallet == 10000 && params.maxTx == 10000) {
walletLtd_ = false;
}
isExempted_[address(this)] = true;
isExempted_[owner_] = true;
isExempted_[spawner_] = true;
isExempted_[address(0x000000000000000000000000000000000000dEaD)] = true;
balances_[address(this)] = supply_; // premine held here for LP seeding
emit OwnershipTransferred(address(0), owner_);
emit IncreasedLimits(params.maxWallet, params.maxTx);
emit Transfer(address(0), address(this), supply_);
}
// =====================================================================
// Mint
// =====================================================================
/// One-time: grant the minter contract exclusive mint rights.
function setMinter(address m) external {
require(msg.sender == owner_ || msg.sender == spawner_, "o");
require(minter_ == address(0), "set");
require(m != address(0), "z");
minter_ = m;
isExempted_[m] = true;
emit MinterSet(m);
}
/// Mint $LOOT. Only the minter; bounded by the cap; makes no external call.
/// Bypasses wallet/tx limits.
function mint(address to, uint256 amount) external {
require(msg.sender == minter_, "mint");
require(to != address(0), "z");
uint256 newSupply = supply_ + amount;
require(newSupply <= cap_, "cap");
supply_ = newSupply;
unchecked { balances_[to] += amount; }
emit Transfer(address(0), to, amount);
}
function minter() external view returns (address) { return minter_; }
function cap() external view returns (uint256) { return cap_; }
// =====================================================================
// Liquidity
// =====================================================================
/// Create the pool and seed the full premine one-sided.
function startTrading(
address pmgr,
uint160 sqrtPriceX96,
INonfungiblePositionManager.MintParams memory params
) external {
require(msg.sender == owner_ || msg.sender == spawner_, "o");
require(!tradingEnabled_, "td");
require(params.recipient == owner_, "rcpt");
require(params.token0 != params.token1, "s1");
require(params.token1 == address(this) || params.token0 == address(this), "s2");
if (params.token1 == address(this)) {
require(params.amount1Desired == supply_, "s6");
} else {
require(params.amount0Desired == supply_, "s6");
}
tradingEnabled_ = true;
tradingEnabledTs_ = block.timestamp;
positionManager_ = INonfungiblePositionManager(pmgr);
isExempted_[pmgr] = true;
allowances_[address(this)][pmgr] = supply_;
uniswapV3Pool_ = positionManager_.createAndInitializePoolIfNecessary(
params.token0, params.token1, params.fee, sqrtPriceX96
);
isExempted_[uniswapV3Pool_] = true;
(lpNFTId_, , , ) = positionManager_.mint(params);
emit StartedTrading(uniswapV3Pool_, lpNFTId_, pmgr);
}
/// Public getters for the dApp: the LP NFT id + pool.
function lpNFTId() external view returns (uint256) { return lpNFTId_; }
function uniswapV3Pool() external view returns (address) { return uniswapV3Pool_; }
// =====================================================================
// anti-snipe limits
// =====================================================================
function increaseLimits(uint256 _maxWallet, uint256 _maxTx) external {
require(msg.sender == owner_ && walletLtd_, "o");
require((supply_ * _maxWallet) / 10000 >= maxWallet_ && _maxWallet <= 10000, "a");
require((supply_ * _maxTx) / 10000 >= maxTx_ && _maxTx <= 10000, "b");
maxWallet_ = (supply_ * _maxWallet) / 10000;
maxTx_ = (supply_ * _maxTx) / 10000;
if (_maxWallet == 10000 && _maxTx == 10000) walletLtd_ = false;
emit IncreasedLimits(_maxWallet, _maxTx);
}
function setExempt(address account, bool exempt) external {
require(msg.sender == owner_, "o");
require(account != address(0), "z");
isExempted_[account] = exempt;
emit ExemptSet(account, exempt);
}
// =====================================================================
// ERC-20
// =====================================================================
function name() external view returns (string memory) { return name_; }
function symbol() external view returns (string memory) { return symbol_; }
function decimals() external view returns (uint8) { return decimals_; }
function totalSupply() external view returns (uint256) { return supply_; }
function balanceOf(address a) external view returns (uint256) { return balances_[a]; }
function allowance(address o, address s) external view returns (uint256) { return allowances_[o][s]; }
function owner() external view returns (address) { return owner_; }
function transfer(address to, uint256 amount) external returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 a = allowances_[from][msg.sender];
if (a != type(uint256).max) {
require(a >= amount, "25");
_approve(from, msg.sender, a - amount);
}
_transfer(from, to, amount);
return true;
}
function _approve(address o, address s, uint256 a) private {
require(o != address(0) && s != address(0), "z");
allowances_[o][s] = a;
emit Approval(o, s, a);
}
function _transfer(address from, address to, uint256 amount) private {
require(from != address(0) && to != address(0), "z");
require(balances_[from] >= amount, "ba");
if (tradingEnabled_ && walletLtd_ && !isExempted_[from] && !isExempted_[to]) {
require(maxTx_ >= amount, "mt");
}
if (tradingEnabled_ && walletLtd_ && !isExempted_[to]) {
require((balances_[to] + amount) <= maxWallet_, "mw");
}
unchecked {
balances_[from] -= amount;
balances_[to] += amount;
}
emit Transfer(from, to, amount);
}
// =====================================================================
// housekeeping
// =====================================================================
function transferOwnership(address newOwner) external {
require(msg.sender == owner_, "o");
require(newOwner != address(0), "z"); // no renounce-to-zero (bricks admin)
emit OwnershipTransferred(owner_, newOwner);
owner_ = newOwner;
}
function recoverEth() external lockTheEntry {
require(msg.sender == owner_, "o");
uint256 bal = address(this).balance;
if (bal > 0) {
(bool ok, ) = owner_.call{value: bal}("");
require(ok, "e");
}
}
/// Recover ERC-20 tokens accidentally sent to this contract, to the owner.
/// Cannot touch this token's own balance (the premine held for seeding).
function recoverERC20(address erc20, uint256 amount) external lockTheEntry {
require(msg.sender == owner_, "o");
require(erc20 != address(this), "self");
(bool ok, bytes memory data) = erc20.call(abi.encodeWithSelector(0xa9059cbb, owner_, amount));
require(ok && (data.length == 0 || abi.decode(data, (bool))), "x");
}
receive() external payable {}
}
Chain explorerunavailableChain node82ms