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

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