FulcrumBondingCurveV2

0x8d5a90d30d83d0a03f0163b941c2a25ea40e3811

Verification
Verified
v0.8.35+commit.47b9dedd
Type
Contract
9,017 bytes
ABI entries
75
44 read · 9 write
License
none

Contract information

Address
0x8d5a90d30d83d0a03f0163b941c2a25ea40e3811
Chain
Robinhood Chain (4663)
Compiler
v0.8.35+commit.47b9dedd
Optimization
Enabled
Creation tx
0x7265dd1cff…ca301330c9

Token

Name
Sofia Vega
Symbol
SVGA
Decimals
18
Holders
3

Read contract (44)

BPS()uint16
BUY_CREATOR_BPS()uint16
BUY_PLATFORM_BPS()uint16
CURVE_ALLOCATION()uint256
DEAD()address
GRADUATION_FEE_BPS()uint16
NPM()address
POOL_FEE()uint24
RESERVE_LP2()uint256
SELL_BURN_BPS()uint16
SELL_CREATOR_BPS()uint16
SELL_PLATFORM_BPS()uint16
TICK_HIGH()int24
TICK_LOW()int24
TOTAL_SUPPLY()uint256
V3_FACTORY()address
VIRTUAL_ETH()uint256
VIRTUAL_TOKEN_RESERVE()uint256
WETH()address
allowance(address, address)uint256
balanceOf(address)uint256
creator()address
creatorRewardsPot()uint256
curveState()uint256, uint256, uint256, uint256, bool, bool
decimals()uint8
devLockedUntilGraduation()bool
factory()address
graduated()bool
graduationTarget()uint256
lastPokeDay()uint256
metadataHash()bytes32
name()string
platformWallet()address
realEthReserve()uint256
reserveLpDeployed()bool
symbol()string
tokenReserve()uint256
tokensSold()uint256
totalBurned()uint256
totalSupply()uint256
v3Pool()address
volumeStreak()uint8
volumeStreakDays()uint8
volumeThresholdWei()uint256

Events (14)

ApprovalBuyCreatorRewardsClaimedDevLockEnabledFeeBurnGraduatedGraduationTargetChangedReserveBurnedReserveLpDeployedSellTransferVirtualEthChangedVolumePokedVolumeThresholdChanged

ABI

[
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "bool",
            "name": "lockDev",
            "type": "bool"
          },
          {
            "internalType": "address",
            "name": "factory",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "totalSupply",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "curveAllocation",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "virtualEth",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "graduationTarget",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "reserveLp2",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "volumeThresholdWei",
            "type": "uint256"
          },
          {
            "internalType": "uint8",
            "name": "volumeStreakDays",
            "type": "uint8"
          },
          {
            "internalType": "bytes32",
            "name": "metadataHash",
            "type": "bytes32"
          }
        ],
        "internalType": "struct FulcrumBondingCurveV2.LaunchArgs",
        "name": "a",
        "type": "tuple"
      }
    ],
    "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"
  },
  {
    "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": "buyer",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokensOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newPriceX96",
        "type": "uint256"
      }
    ],
    "name": "Buy",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "burned",
        "type": "uint256"
      }
    ],
    "name": "CreatorRewardsClaimed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "DevLockEnabled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "reason",
        "type": "string"
      }
    ],
    "name": "FeeBurn",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethSeeded",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "lpTokens",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "overhangBurned",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "platformFee",
        "type": "uint256"
      }
    ],
    "name": "Graduated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newTarget",
        "type": "uint256"
      }
    ],
    "name": "GraduationTargetChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "ReserveBurned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokens",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "eth",
        "type": "uint256"
      }
    ],
    "name": "ReserveLpDeployed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "seller",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokensIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newPriceX96",
        "type": "uint256"
      }
    ],
    "name": "Sell",
    "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": false,
        "internalType": "uint256",
        "name": "newVE",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newVirtualTokenReserve",
        "type": "uint256"
      }
    ],
    "name": "VirtualEthChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "dailyVolume",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint8",
        "name": "streak",
        "type": "uint8"
      }
    ],
    "name": "VolumePoked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "wei_",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint8",
        "name": "streak",
        "type": "uint8"
      }
    ],
    "name": "VolumeThresholdChanged",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "BUY_CREATOR_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "BUY_PLATFORM_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CURVE_ALLOCATION",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEAD",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "GRADUATION_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "NPM",
    "outputs": [
      {
        "internalType": "contract INonfungiblePositionManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POOL_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "RESERVE_LP2",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SELL_BURN_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SELL_CREATOR_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SELL_PLATFORM_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TICK_HIGH",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TICK_LOW",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TOTAL_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "V3_FACTORY",
    "outputs": [
      {
        "internalType": "contract IUniswapV3Factory",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "VIRTUAL_ETH",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "VIRTUAL_TOKEN_RESERVE",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "WETH",
    "outputs": [
      {
        "internalType": "contract IWETH9",
        "name": "",
        "type": "address"
      }
    ],
    "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": "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": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "buyFor",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "claimCreatorRewards",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "creator",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "creatorRewardsPot",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "curveState",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "_realEth",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "_tokenReserve",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "_virtualEth",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "_graduationTarget",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "_graduated",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "_devLocked",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "devLockedUntilGraduation",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "graduate",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "graduated",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "graduationTarget",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastPokeDay",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lockDevUntilGraduation",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "metadataHash",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "platformWallet",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "realEthReserve",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "reserveLpDeployed",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokensIn",
        "type": "uint256"
      }
    ],
    "name": "sell",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "ethOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newVE",
        "type": "uint256"
      }
    ],
    "name": "setVirtualEth",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tokenReserve",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tokensSold",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalBurned",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "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": [],
    "name": "v3Pool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "volumeStreak",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "volumeStreakDays",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "volumeThresholdWei",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

// ---------------------------------------------------------------------------
// Uniswap V3 minimal interfaces (Robinhood Chain)
// ---------------------------------------------------------------------------
interface IWETH9 {
    function deposit() external payable;
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address) external view returns (uint256);
}
interface IUniswapV3Factory {
    function createPool(address a, address b, uint24 fee) external returns (address);
    function getPool(address a, address b, uint24 fee) external view returns (address);
}
interface IUniswapV3Pool {
    function initialize(uint160 sqrtPriceX96) external;
    function slot0() external view returns (uint160 sqrtPriceX96, int24, uint16, uint16, uint16, uint8, bool);
}
interface INonfungiblePositionManager {
    struct MintParams {
        address token0; address token1; uint24 fee;
        int24 tickLower; int24 tickUpper;
        uint256 amount0Desired; uint256 amount1Desired;
        uint256 amount0Min; uint256 amount1Min;
        address recipient; uint256 deadline;
    }
    function mint(MintParams calldata p) external payable
        returns (uint256, uint128, uint256, uint256);
}
interface IFulcrumFactoryV2 {
    function platformWallet() external view returns (address);
    function owner() external view returns (address);
}

// ---------------------------------------------------------------------------
// FulcrumFactoryV2 — UUPS-upgradeable deployer.
// V3 semantics (in-place rewrite of the same contract name for ABI
// continuity):
//   * Virtual token reserve model: curve prices with a synthetic token
//     reserve so the last real token sells for the exact ETH target.
//   * NEVER burns real tokens. Graduation trigger is "all curve tokens
//     sold" (tokensSold == curveAllocation), not an ETH threshold.
//   * Fixed LP allocation of `lpTokens` (defaults to 206.9M) + ALL raised
//     ETH seeded into ONE Uniswap V3 pool at grad. No second LP, no
//     volume oracle.
//   * Sell fee reduced to 0.5% (0.2% creator + 0.3% platform) — no
//     curve-backing burn.
//
// MAINNET BUILD — the graduation LP NFT is minted to the DEAD address
// so liquidity is permanently locked. Nobody can ever pull LP. There
// is no emergency-withdraw hatch: raised ETH only leaves the curve via
// user sells, or by being paired into the burned LP at graduation.
// ---------------------------------------------------------------------------
contract FulcrumFactoryV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable {
    address public platformWallet;
    uint256 public launchFee;

    // Defaults for newly launched tokens
    uint256 public defaultTotalSupply;         // e.g. 1_000_000_000 ether
    uint256 public defaultCurveAllocation;     // e.g. 793_100_000 ether (must be < totalSupply)
    uint256 public defaultVirtualEth;          // e.g. 1.4 ether
    uint256 public defaultGraduationTarget;    // e.g. 3.92 ether — display target; also derives virtual token reserve
    uint256 public defaultReserveLp2;          // LP token allocation (e.g. 206.9M) — added to ALL raised ETH at grad
    uint256 public defaultVolumeThresholdWei;  // (unused in V3 — retained for storage layout compat)
    uint8   public defaultVolumeStreakDays;    // (unused in V3 — retained for storage layout compat)

    event TokenLaunched(address indexed tokenAddress, address indexed creator);
    event InitialBuy(address indexed tokenAddress, address indexed creator, uint256 ethIn, uint256 tokensOut);
    event PlatformWalletChanged(address indexed w);
    event LaunchFeeChanged(uint256 f);
    event DefaultsChanged();

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() { _disableInitializers(); }

    function initialize(
        address _owner,
        address _platform,
        uint256 _launchFee,
        uint256 _totalSupply,
        uint256 _curveAllocation,
        uint256 _virtualEth,
        uint256 _graduationTarget,
        uint256 _reserveLp2,
        uint256 _volumeThresholdWei,
        uint8   _volumeStreakDays
    ) external initializer {
        __Ownable_init(_owner);
        platformWallet = _platform;
        launchFee = _launchFee;
        defaultTotalSupply = _totalSupply;
        defaultCurveAllocation = _curveAllocation;
        defaultVirtualEth = _virtualEth;
        defaultGraduationTarget = _graduationTarget;
        defaultReserveLp2 = _reserveLp2;
        defaultVolumeThresholdWei = _volumeThresholdWei;
        defaultVolumeStreakDays = _volumeStreakDays;
    }
    function _authorizeUpgrade(address) internal override onlyOwner {}

    function setPlatformWallet(address w) external onlyOwner {
        require(w != address(0), "zero"); platformWallet = w; emit PlatformWalletChanged(w);
    }
    function setLaunchFee(uint256 f) external onlyOwner { launchFee = f; emit LaunchFeeChanged(f); }

    /// Update defaults for FUTURE launches. Pass 0 on any field to leave it unchanged.
    function setDefaults(
        uint256 _totalSupply,
        uint256 _curveAllocation,
        uint256 _virtualEth,
        uint256 _graduationTarget,
        uint256 _reserveLp2,
        uint256 _volumeThresholdWei,
        uint8   _volumeStreakDays
    ) external onlyOwner {
        if (_totalSupply != 0)        defaultTotalSupply = _totalSupply;
        if (_curveAllocation != 0)    defaultCurveAllocation = _curveAllocation;
        if (_virtualEth != 0)         defaultVirtualEth = _virtualEth;
        if (_graduationTarget != 0)   defaultGraduationTarget = _graduationTarget;
        if (_reserveLp2 != 0)         defaultReserveLp2 = _reserveLp2;
        if (_volumeThresholdWei != 0) defaultVolumeThresholdWei = _volumeThresholdWei;
        if (_volumeStreakDays != 0)   defaultVolumeStreakDays = _volumeStreakDays;
        emit DefaultsChanged();
    }

    // Per-token owner-only overrides (proxy calls)
    function setTokenGraduationTarget(address, uint256) external view onlyOwner { revert("disabled in v3"); }
    function setTokenVolumeThreshold(address, uint256, uint8) external view onlyOwner { revert("disabled in v3"); }
    function pokeVolume(address, uint256) external view onlyOwner { revert("disabled in v3"); }
    function forceDeployReserveLP(address) external payable onlyOwner { revert("disabled in v3"); }
    function burnReserveLP(address) external view onlyOwner { revert("disabled in v3"); }

    function launchToken(string memory name, string memory symbol, bool lockDev, bytes32 salt, bytes32 metadataHash)
        external payable returns (address) { return _launch(name, symbol, lockDev, salt, metadataHash); }

    function launchTokenAndBuy(string memory name, string memory symbol, bool lockDev, bytes32 salt, bytes32 metadataHash)
        external payable returns (address token)
    {
        token = _launch(name, symbol, lockDev, salt, metadataHash);
        uint256 buyAmount = msg.value - launchFee;
        if (buyAmount > 0) {
            uint256 out = FulcrumBondingCurveV2(payable(token)).buyFor{value: buyAmount}(msg.sender);
            emit InitialBuy(token, msg.sender, buyAmount, out);
        }
    }

    function _launch(string memory name, string memory symbol, bool lockDev, bytes32 salt, bytes32 metadataHash) internal returns (address) {
        require(msg.value >= launchFee, "Pay launch fee");
        require(defaultGraduationTarget > 0, "target unset");
        (bool ok, ) = payable(platformWallet).call{value: launchFee}("");
        require(ok, "fee xfer");
        FulcrumBondingCurveV2.LaunchArgs memory a = FulcrumBondingCurveV2.LaunchArgs({
            name: name, symbol: symbol, creator: msg.sender, lockDev: lockDev, factory: address(this),
            totalSupply: defaultTotalSupply,
            curveAllocation: defaultCurveAllocation,
            virtualEth: defaultVirtualEth,
            graduationTarget: defaultGraduationTarget,
            reserveLp2: defaultReserveLp2,
            volumeThresholdWei: defaultVolumeThresholdWei,
            volumeStreakDays: defaultVolumeStreakDays,
            metadataHash: metadataHash
        });
        FulcrumBondingCurveV2 t = new FulcrumBondingCurveV2{salt: salt}(a);
        emit TokenLaunched(address(t), msg.sender);
        return address(t);
    }

    function predictTokenAddress(
        string memory name, string memory symbol, address creator, bool lockDev, bytes32 salt, bytes32 metadataHash
    ) external view returns (address) {
        FulcrumBondingCurveV2.LaunchArgs memory a = FulcrumBondingCurveV2.LaunchArgs({
            name: name, symbol: symbol, creator: creator, lockDev: lockDev, factory: address(this),
            totalSupply: defaultTotalSupply,
            curveAllocation: defaultCurveAllocation,
            virtualEth: defaultVirtualEth,
            graduationTarget: defaultGraduationTarget,
            reserveLp2: defaultReserveLp2,
            volumeThresholdWei: defaultVolumeThresholdWei,
            volumeStreakDays: defaultVolumeStreakDays,
            metadataHash: metadataHash
        });
        bytes memory bytecode = abi.encodePacked(
            type(FulcrumBondingCurveV2).creationCode,
            abi.encode(a)
        );
        bytes32 h = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(bytecode)));
        return address(uint160(uint256(h)));
    }
}

// ---------------------------------------------------------------------------
// FulcrumBondingCurveV2 — V3 semantics
//
// Pricing uses a *virtual* token reserve derived from the pair
// (curveAllocation, virtualEth, graduationTarget) so that:
//     VIRTUAL_TOKEN_RESERVE = curveAllocation * (virtualEth + graduationTarget)
//                             / graduationTarget
//
// At tokensSold == curveAllocation the real ETH raised equals
// graduationTarget exactly, and the final curve price matches the price
// implied by (lpTokens, ETH raised) → LP opens without any handoff dump.
//
// - No burns. Real supply is fixed at TOTAL_SUPPLY (1B) forever.
// - Graduation triggers when the FULL curveAllocation has been sold
//   (not on an ETH threshold), so the curve always ends with 0 tokens
//   left for sale on the curve.
// - At graduation, all raised ETH (minus 0.5% platform fee) is paired
//   with the fixed `LP_TOKENS` allocation into a single Uniswap V3 pool.
//   The LP NFT is minted to platformWallet() (test build).
// - Sell fee: 0.5% total = 0.2% creator + 0.3% platform. No curve-backing burn.
// ---------------------------------------------------------------------------
contract FulcrumBondingCurveV2 is ERC20 {
    struct LaunchArgs {
        string name; string symbol; address creator; bool lockDev; address factory;
        uint256 totalSupply;
        uint256 curveAllocation;
        uint256 virtualEth;
        uint256 graduationTarget;
        uint256 reserveLp2;
        uint256 volumeThresholdWei;
        uint8   volumeStreakDays;
        bytes32 metadataHash;
    }

    // -------- Constants (fees, addresses) --------
    uint16 public constant BUY_CREATOR_BPS   = 10;   // 0.1%
    uint16 public constant BUY_PLATFORM_BPS  = 20;   // 0.2%
    uint16 public constant SELL_CREATOR_BPS  = 20;   // 0.2%
    uint16 public constant SELL_PLATFORM_BPS = 30;   // 0.3%
    uint16 public constant SELL_BURN_BPS     = 0;    // removed in V3
    uint16 public constant GRADUATION_FEE_BPS= 50;   // 0.5% ETH
    uint16 public constant BPS = 10_000;

    IWETH9 public constant WETH = IWETH9(0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73);
    IUniswapV3Factory public constant V3_FACTORY =
        IUniswapV3Factory(0x1f7d7550B1b028f7571E69A784071F0205FD2EfA);
    INonfungiblePositionManager public constant NPM =
        INonfungiblePositionManager(0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3);
    uint24  public constant POOL_FEE  = 100;   // 0.01% tier, tickSpacing 1
    int24   public constant TICK_LOW  = -887272;
    int24   public constant TICK_HIGH =  887272;
    address public constant DEAD      = 0x000000000000000000000000000000000000dEaD;

    // -------- Per-token immutables --------
    address public immutable creator;
    address public immutable factory;
    bytes32 public immutable metadataHash;
    uint256 public immutable TOTAL_SUPPLY;
    uint256 public immutable CURVE_ALLOCATION;
    /// Mutable so factory owner can retune pricing BEFORE any buy happens
    /// (e.g. ETH price swings between deploy and first trade). Locked after
    /// the first buy — see setVirtualEth().
    uint256 public VIRTUAL_ETH;
    /// Fixed LP token allocation seeded at graduation (paired with all raised ETH).
    uint256 public immutable RESERVE_LP2;
    /// Virtual token reserve used only for AMM pricing. Never minted; never burned.
    /// Recomputed alongside VIRTUAL_ETH via setVirtualEth().
    uint256 public VIRTUAL_TOKEN_RESERVE;

    // -------- Mutable state --------
    /// Kept for ABI/UI compat. Represents the ETH raised when the FULL
    /// curveAllocation has been sold (i.e. the intrinsic ETH the curve
    /// yields at grad). In V3 the actual trigger is tokensSold == curveAllocation.
    uint256 public graduationTarget;
    bool    public devLockedUntilGraduation;
    bool    public graduated;
    address public v3Pool;

    uint256 public realEthReserve;
    /// Virtual token side of the AMM invariant (starts at VIRTUAL_TOKEN_RESERVE
    /// and decreases with buys). Exposed under the legacy name for UI compat.
    uint256 public tokenReserve;
    /// Real tokens actually transferred out of the curve to buyers.
    /// Capped at CURVE_ALLOCATION. Graduation triggers when it hits the cap.
    uint256 public tokensSold;
    uint256 public creatorRewardsPot;
    uint256 public totalBurned; // stays 0 in V3, kept for ABI compat

    // Legacy fields — unused in V3, retained for storage-layout / ABI compat
    uint256 public volumeThresholdWei;
    uint8   public volumeStreakDays;
    uint8   public volumeStreak;
    uint256 public lastPokeDay;
    bool    public reserveLpDeployed;

    event Buy(address indexed buyer, uint256 ethIn, uint256 tokensOut, uint256 newPriceX96);
    event Sell(address indexed seller, uint256 tokensIn, uint256 ethOut, uint256 newPriceX96);
    event CreatorRewardsClaimed(address indexed creator, uint256 amount, uint256 burned);
    event FeeBurn(uint256 amount, string reason);
    event Graduated(address indexed pool, uint256 ethSeeded, uint256 lpTokens, uint256 overhangBurned, uint256 platformFee);
    event DevLockEnabled();
    event GraduationTargetChanged(uint256 newTarget);
    event VolumeThresholdChanged(uint256 wei_, uint8 streak);
    event VolumePoked(uint256 dailyVolume, uint8 streak);
    event ReserveLpDeployed(uint256 tokens, uint256 eth);
    event ReserveBurned(uint256 amount);
    event VirtualEthChanged(uint256 newVE, uint256 newVirtualTokenReserve);

    constructor(LaunchArgs memory a) ERC20(a.name, a.symbol) {
        require(a.graduationTarget > 0, "target=0");
        require(a.virtualEth > 0, "virtual=0");
        require(a.totalSupply > 0, "supply=0");
        require(a.curveAllocation > 0 && a.reserveLp2 > 0
            && a.curveAllocation + a.reserveLp2 <= a.totalSupply, "alloc");
        creator = a.creator;
        factory = a.factory;
        devLockedUntilGraduation = a.lockDev;
        metadataHash = a.metadataHash;
        TOTAL_SUPPLY = a.totalSupply;
        CURVE_ALLOCATION = a.curveAllocation;
        VIRTUAL_ETH = a.virtualEth;
        RESERVE_LP2 = a.reserveLp2;
        graduationTarget = a.graduationTarget;
        volumeThresholdWei = a.volumeThresholdWei;
        volumeStreakDays = a.volumeStreakDays;
        // VIRTUAL_TOKEN_RESERVE = curveAllocation * (virtualEth + graduationTarget) / graduationTarget
        // With this, at tokensSold == curveAllocation, realEth raised == graduationTarget exactly.
        VIRTUAL_TOKEN_RESERVE = (a.curveAllocation * (a.virtualEth + a.graduationTarget)) / a.graduationTarget;
        tokenReserve = VIRTUAL_TOKEN_RESERVE;
        _mint(address(this), a.totalSupply);
    }

    function platformWallet() public view returns (address) {
        return IFulcrumFactoryV2(factory).platformWallet();
    }

    /// Factory owner can retune the virtual ETH reserve BEFORE any buy has
    /// happened. This exists so pricing can be adjusted if ETH's USD price
    /// swings materially between deploy and first trade. Locked forever
    /// after the first buy so live traders are never repriced.
    function setVirtualEth(uint256 newVE) external {
        require(msg.sender == IFulcrumFactoryV2(factory).owner(), "not owner");
        require(!graduated, "graduated");
        require(tokensSold == 0, "trading started");
        require(newVE > 0, "zero");
        VIRTUAL_ETH = newVE;
        VIRTUAL_TOKEN_RESERVE = (CURVE_ALLOCATION * (newVE + graduationTarget)) / graduationTarget;
        tokenReserve = VIRTUAL_TOKEN_RESERVE;
        emit VirtualEthChanged(newVE, VIRTUAL_TOKEN_RESERVE);
    }

    // ============ BUY ============
    receive() external payable { _buy(msg.sender); }
    function buyFor(address to) external payable returns (uint256) { return _buy(to); }

    function _buy(address to) internal returns (uint256 tokensOut) {
        require(!graduated, "Graduated");
        require(msg.value > 0, "No ETH");

        uint256 creatorFee  = (msg.value * BUY_CREATOR_BPS)  / BPS;
        uint256 platformFee = (msg.value * BUY_PLATFORM_BPS) / BPS;
        uint256 ethAfterFee = msg.value - creatorFee - platformFee;

        uint256 xVirt = VIRTUAL_ETH + realEthReserve;
        uint256 k = xVirt * tokenReserve;
        uint256 newX = xVirt + ethAfterFee;
        uint256 newY = k / newX;
        tokensOut = tokenReserve - newY;
        require(tokensOut > 0, "Zero out");

        // Cap at remaining real curve allocation; refund excess ETH.
        uint256 remaining = CURVE_ALLOCATION - tokensSold;
        require(remaining > 0, "Curve full");
        uint256 refund = 0;
        if (tokensOut > remaining) {
            // Solve for exact ETH needed for `remaining` tokens: newY_target = tokenReserve - remaining.
            uint256 targetY = tokenReserve - remaining;
            uint256 targetX = k / targetY;
            uint256 ethNeeded = targetX - xVirt;
            // Re-derive fees from ethNeeded so the invariant holds exactly.
            uint256 grossNeeded = (ethNeeded * BPS) / (BPS - BUY_CREATOR_BPS - BUY_PLATFORM_BPS) + 1;
            if (grossNeeded > msg.value) grossNeeded = msg.value;
            creatorFee  = (grossNeeded * BUY_CREATOR_BPS)  / BPS;
            platformFee = (grossNeeded * BUY_PLATFORM_BPS) / BPS;
            ethAfterFee = grossNeeded - creatorFee - platformFee;
            refund = msg.value - grossNeeded;
            tokensOut = remaining;
        }

        realEthReserve += ethAfterFee;
        tokenReserve   -= tokensOut;
        tokensSold     += tokensOut;

        creatorRewardsPot += creatorFee;
        (bool okP, ) = payable(platformWallet()).call{value: platformFee}("");
        require(okP, "plat xfer");
        if (refund > 0) {
            (bool okR, ) = payable(to).call{value: refund}("");
            require(okR, "refund xfer");
        }

        _transfer(address(this), to, tokensOut);
        emit Buy(to, msg.value, tokensOut, _priceX96());

        if (!graduated && tokensSold >= CURVE_ALLOCATION) {
            try this.graduate() { } catch { }
        }
    }

    // ============ SELL ============
    function sell(uint256 tokensIn) external returns (uint256 ethOut) {
        require(!graduated, "Graduated");
        require(tokensIn > 0, "Zero");
        if (msg.sender == creator && devLockedUntilGraduation) revert("Dev locked until graduation");
        require(balanceOf(msg.sender) >= tokensIn, "Bal");
        require(tokensIn <= tokensSold, "Exceeds sold");

        uint256 xVirt = VIRTUAL_ETH + realEthReserve;
        uint256 k = xVirt * tokenReserve;
        uint256 newY = tokenReserve + tokensIn;
        uint256 newX = k / newY;
        uint256 ethOutGross = xVirt - newX;
        require(ethOutGross > 0 && realEthReserve > 1, "Curve");
        if (ethOutGross >= realEthReserve) ethOutGross = realEthReserve - 1;

        uint256 creatorFee  = (ethOutGross * SELL_CREATOR_BPS)  / BPS;
        uint256 platformFee = (ethOutGross * SELL_PLATFORM_BPS) / BPS;
        ethOut = ethOutGross - creatorFee - platformFee;

        tokenReserve   += tokensIn;
        tokensSold     -= tokensIn;
        realEthReserve -= (ethOut + creatorFee + platformFee);

        _transfer(msg.sender, address(this), tokensIn);

        creatorRewardsPot += creatorFee;
        (bool okP, ) = payable(platformWallet()).call{value: platformFee}("");
        require(okP, "plat xfer");

        (bool s, ) = payable(msg.sender).call{value: ethOut}("");
        require(s, "eth xfer");
        emit Sell(msg.sender, tokensIn, ethOut, _priceX96());
    }

    // ============ CREATOR CLAIM ============
    function claimCreatorRewards() external {
        require(msg.sender == creator, "Not creator");
        uint256 amt = creatorRewardsPot;
        require(amt > 0, "Nothing");
        creatorRewardsPot = 0;
        (bool ok, ) = payable(msg.sender).call{value: amt}("");
        require(ok, "claim xfer");
        emit CreatorRewardsClaimed(msg.sender, amt, 0);
    }

    function _update(address from, address to, uint256 value) internal override {
        if (from == creator && devLockedUntilGraduation && !graduated
            && from != address(0) && to != address(this) && to != address(0)) {
            revert("Dev tokens locked until graduation");
        }
        super._update(from, to, value);
    }

    function lockDevUntilGraduation() external {
        require(msg.sender == creator, "Not creator");
        require(!graduated, "Graduated");
        devLockedUntilGraduation = true;
        emit DevLockEnabled();
    }

    // ============ GRADUATION (V3 — fixed LP alloc, no burns) ============
    function graduate() external {
        require(!graduated, "Already");
        require(tokensSold >= CURVE_ALLOCATION, "Curve not sold out");
        graduated = true;

        uint256 ethPot = realEthReserve;
        realEthReserve = 0;
        tokenReserve   = 0;

        // 0.5% grad fee in ETH
        uint256 platformFee = (ethPot * GRADUATION_FEE_BPS) / BPS;
        (bool okFee, ) = payable(platformWallet()).call{value: platformFee}("");
        require(okFee, "plat xfer");
        uint256 ethForLP = ethPot - platformFee;

        // Fixed LP allocation (206.9M). Contract still holds it — never touched during the curve.
        uint256 lpTokens = RESERVE_LP2;
        require(balanceOf(address(this)) >= lpTokens, "lp bal");

        WETH.deposit{value: ethForLP}();

        address tokenA = address(this);
        address tokenB = address(WETH);
        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        (uint256 amount0, uint256 amount1) = tokenA < tokenB
            ? (lpTokens, ethForLP)
            : (ethForLP, lpTokens);

        address pool = V3_FACTORY.getPool(token0, token1, POOL_FEE);
        if (pool == address(0)) pool = V3_FACTORY.createPool(token0, token1, POOL_FEE);
        v3Pool = pool;
        IUniswapV3Pool(pool).initialize(_sqrtPriceX96(amount0, amount1));

        _approve(address(this), address(NPM), lpTokens);
        IWETH9(address(WETH)).approve(address(NPM), ethForLP);

        INonfungiblePositionManager.MintParams memory p = INonfungiblePositionManager.MintParams({
            token0: token0, token1: token1, fee: POOL_FEE,
            tickLower: TICK_LOW, tickUpper: TICK_HIGH,
            amount0Desired: amount0, amount1Desired: amount1,
            amount0Min: 0, amount1Min: 0,
            recipient: DEAD, deadline: block.timestamp + 300
        });
        NPM.mint(p);
        emit Graduated(pool, ethForLP, lpTokens, 0, platformFee);
    }

    // ============ VIEWS ============
    function _priceX96() internal view returns (uint256) {
        if (tokenReserve == 0) return 0;
        uint256 ratio = ((VIRTUAL_ETH + realEthReserve) * 1e18) / tokenReserve;
        return _sqrt(ratio) * (1 << 48);
    }
    function _sqrtPriceX96(uint256 amount0, uint256 amount1) internal pure returns (uint160) {
        uint256 num = amount1 * (1 << 96);
        uint256 ratio = num / amount0;
        return uint160(_sqrt(ratio) * (1 << 48));
    }
    function _sqrt(uint256 x) internal pure returns (uint256 y) {
        if (x == 0) return 0;
        uint256 z = (x + 1) / 2; y = x;
        while (z < y) { y = z; z = (x / z + z) / 2; }
    }
    function curveState() external view returns (
        uint256 _realEth, uint256 _tokenReserve, uint256 _virtualEth,
        uint256 _graduationTarget, bool _graduated, bool _devLocked
    ) {
        return (realEthReserve, tokenReserve, VIRTUAL_ETH, graduationTarget, graduated, devLockedUntilGraduation);
    }

}
Chain explorer1319msChain node79ms