Orchard

0xebb8b167c0992cfdc497a995a8cf7167acaa0a1a

Verification
Verified
v0.8.30+commit.73712a01
Type
Contract
13,588 bytes
ABI entries
103
40 read · 19 write
License
none

Contract information

Address
0xebb8b167c0992cfdc497a995a8cf7167acaa0a1a
Chain
Robinhood Chain (4663)
Compiler
v0.8.30+commit.73712a01
Optimization
Enabled
Creation tx
0x4367ef2ca9…4e4c7cc89b

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (40)

JUICE_BPS()uint16
MAX_ADMIN_FEE_BPS()uint16
MAX_MIN_PLANT_ETH()uint256
MAX_PLANT_SLOTS()uint256
MAX_RAKE_BPS()uint16
PLOTS()uint8
ROUND_SECONDS()uint256
SOW_SECONDS()uint256
STAKERS_BPS()uint16
aapl()address
adminAccrued()uint256
adminFeeBps()uint16
currentRound()uint256
genesis()uint256
jackpotAccrued()uint256
jackpotBps()uint16
jackpotOdds()uint32
juiceIndex()uint256
keeper()address
minPlantEth()uint256
miners(uint256)address[]
nextToResolve()uint256
owner()address
pendingClaim(address, uint256[])uint256
pendingOwner()address
playerPlots(uint256, address)uint32
playerTotal(uint256, address)uint256
plotStake(uint256, uint8)uint256
rakeBps()uint16
randomness()address
renounceOwnership()
roundClaimed(uint256, address)bool
rounds(uint256)uint64, bool, bool, uint8, uint16, uint16, uint32, uint256, uint256, uint256, uint256, uint256
seed()address
stakeOf(uint256, address, uint8)uint256
staking()address
stakingAccrued()uint256
swapper()address
treasuryAccrued()uint256
unclaimedShares()uint256

Events (23)

AdminCollectedAdminFeeBpsSetBuybackBurnedClaimedCropVaultedJackpotBpsSetJackpotHitJackpotOddsSetKeeperSetMinPlantEthSetOwnershipTransferStartedOwnershipTransferredPlantedPlantedManyRakeBpsSetRandomnessSetRevealedRoundVoidedSealedStakingFlushedStakingSetSwapperSetTreasuryCollected

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "aapl_",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "seed_",
        "type": "address"
      },
      {
        "internalType": "contract ISwapper",
        "name": "swapper_",
        "type": "address"
      },
      {
        "internalType": "contract IRandomness",
        "name": "randomness_",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "rakeBps_",
        "type": "uint16"
      },
      {
        "internalType": "address",
        "name": "owner_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      }
    ],
    "name": "AlreadyClaimed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BelowMinPlant",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BpsTooHigh",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientOutput",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPlot",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPlotMask",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidRoundCount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MinPlantTooHigh",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotAuthorized",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NothingToClaim",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "RenounceDisabled",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      }
    ],
    "name": "RoundNotResolved",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SowingClosed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TooManySlots",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroOdds",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "aaplAmount",
        "type": "uint256"
      }
    ],
    "name": "AdminCollected",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "AdminFeeBpsSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "aaplIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "seedBurned",
        "type": "uint256"
      }
    ],
    "name": "BuybackBurned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "player",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "aaplAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "juiceFee",
        "type": "uint256"
      }
    ],
    "name": "Claimed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint8",
        "name": "winningPlot",
        "type": "uint8"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "crop",
        "type": "uint256"
      }
    ],
    "name": "CropVaulted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "JackpotBpsSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "JackpotHit",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint32",
        "name": "odds",
        "type": "uint32"
      }
    ],
    "name": "JackpotOddsSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "keeper",
        "type": "address"
      }
    ],
    "name": "KeeperSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "minPlantEth",
        "type": "uint256"
      }
    ],
    "name": "MinPlantEthSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferStarted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "player",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint8",
        "name": "plot",
        "type": "uint8"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "stake",
        "type": "uint256"
      }
    ],
    "name": "Planted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "player",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "startRound",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "roundCount",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint32",
        "name": "plotMask",
        "type": "uint32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "totalStake",
        "type": "uint256"
      }
    ],
    "name": "PlantedMany",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "RakeBpsSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "randomness",
        "type": "address"
      }
    ],
    "name": "RandomnessSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint8",
        "name": "winningPlot",
        "type": "uint8"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "winningStake",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "netLossPool",
        "type": "uint256"
      }
    ],
    "name": "Revealed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      }
    ],
    "name": "RoundVoided",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "sealBlock",
        "type": "uint64"
      }
    ],
    "name": "Sealed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "aaplAmount",
        "type": "uint256"
      }
    ],
    "name": "StakingFlushed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "staking",
        "type": "address"
      }
    ],
    "name": "StakingSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "swapper",
        "type": "address"
      }
    ],
    "name": "SwapperSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "aaplAmount",
        "type": "uint256"
      }
    ],
    "name": "TreasuryCollected",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "JUICE_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_ADMIN_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_MIN_PLANT_ETH",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_PLANT_SLOTS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_RAKE_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PLOTS",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ROUND_SECONDS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SOW_SECONDS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "STAKERS_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "aapl",
    "outputs": [
      {
        "internalType": "contract IERC20",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "adminAccrued",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "adminFeeBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "minSeedOut",
        "type": "uint256"
      }
    ],
    "name": "buybackSeed",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256[]",
        "name": "roundIds",
        "type": "uint256[]"
      }
    ],
    "name": "claim",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "collectAdmin",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "collectTreasury",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "maxRounds",
        "type": "uint256"
      }
    ],
    "name": "crank",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "currentRound",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "flushStaking",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "genesis",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "jackpotAccrued",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "jackpotBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "jackpotOdds",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "juiceIndex",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "keeper",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "minPlantEth",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "round",
        "type": "uint256"
      }
    ],
    "name": "miners",
    "outputs": [
      {
        "internalType": "address[]",
        "name": "",
        "type": "address[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "nextToResolve",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "player",
        "type": "address"
      },
      {
        "internalType": "uint256[]",
        "name": "roundIds",
        "type": "uint256[]"
      }
    ],
    "name": "pendingClaim",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "total",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint8",
        "name": "plot",
        "type": "uint8"
      },
      {
        "internalType": "uint256",
        "name": "minAaplOut",
        "type": "uint256"
      }
    ],
    "name": "plant",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint32",
        "name": "plotMask",
        "type": "uint32"
      },
      {
        "internalType": "uint16",
        "name": "roundCount",
        "type": "uint16"
      },
      {
        "internalType": "uint256",
        "name": "minAaplOut",
        "type": "uint256"
      }
    ],
    "name": "plantMany",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "playerPlots",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "playerTotal",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "name": "plotStake",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "rakeBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "randomness",
    "outputs": [
      {
        "internalType": "contract IRandomness",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "roundClaimed",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "rounds",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "sealBlock",
        "type": "uint64"
      },
      {
        "internalType": "bool",
        "name": "revealed",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "voided",
        "type": "bool"
      },
      {
        "internalType": "uint8",
        "name": "winningPlot",
        "type": "uint8"
      },
      {
        "internalType": "uint16",
        "name": "rakeBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "jackpotBps",
        "type": "uint16"
      },
      {
        "internalType": "uint32",
        "name": "jackpotOdds",
        "type": "uint32"
      },
      {
        "internalType": "uint256",
        "name": "totalStake",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "winningStake",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "netLossPool",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "entryIndex",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "jackpotWon",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "seed",
    "outputs": [
      {
        "internalType": "contract IERC20",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "setAdminFeeBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "setJackpotBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint32",
        "name": "odds",
        "type": "uint32"
      }
    ],
    "name": "setJackpotOdds",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "keeper_",
        "type": "address"
      }
    ],
    "name": "setKeeper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "minPlantEth_",
        "type": "uint256"
      }
    ],
    "name": "setMinPlantEth",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "setRakeBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IRandomness",
        "name": "randomness_",
        "type": "address"
      }
    ],
    "name": "setRandomness",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IStaking",
        "name": "staking_",
        "type": "address"
      }
    ],
    "name": "setStaking",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract ISwapper",
        "name": "swapper_",
        "type": "address"
      }
    ],
    "name": "setSwapper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "name": "stakeOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "staking",
    "outputs": [
      {
        "internalType": "contract IStaking",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "stakingAccrued",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "swapper",
    "outputs": [
      {
        "internalType": "contract ISwapper",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "treasuryAccrued",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "unclaimedShares",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

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

//
//                         ,(
//                        ((
//                         \\
//                     .---\\---.
//                    /    ``    \
//                   |            |
//                   |            |
//                    \          /
//                     '.______.'
//
//              O R C H A R D  ·  $SEED
//             Plant ETH. Harvest $AAPL.
//
//                https://orchard.gold
//            https://x.com/orcharddotgold
//

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable, Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ISwapper} from "./interfaces/ISwapper.sol";
import {IStaking} from "./interfaces/IStaking.sol";
import {IRandomness} from "./interfaces/IRandomness.sol";

contract Orchard is Ownable2Step, ReentrancyGuard {
    using SafeERC20 for IERC20;

    error InvalidPlot();
    error InvalidPlotMask();
    error InvalidRoundCount();
    error TooManySlots();
    error BelowMinPlant();
    error SowingClosed();
    error RoundNotResolved(uint256 round);
    error AlreadyClaimed(uint256 round);
    error NothingToClaim();
    error InsufficientOutput();
    error ZeroAddress();
    error BpsTooHigh();
    error ZeroOdds();
    error NotAuthorized();
    error MinPlantTooHigh();
    error RenounceDisabled();

    event Planted(address indexed player, uint256 indexed round, uint8 plot, uint256 ethIn, uint256 stake);
    event PlantedMany(
        address indexed player,
        uint256 indexed startRound,
        uint16 roundCount,
        uint32 plotMask,
        uint256 ethIn,
        uint256 totalStake
    );
    event Sealed(uint256 indexed round, uint64 sealBlock);
    event Revealed(uint256 indexed round, uint8 winningPlot, uint256 winningStake, uint256 netLossPool);
    event CropVaulted(uint256 indexed round, uint8 winningPlot, uint256 crop);
    event RoundVoided(uint256 indexed round);
    event JackpotHit(uint256 indexed round, uint256 amount);
    event Claimed(address indexed player, uint256 aaplAmount, uint256 juiceFee);
    event BuybackBurned(uint256 aaplIn, uint256 seedBurned);
    event TreasuryCollected(address indexed to, uint256 aaplAmount);
    event AdminCollected(address indexed to, uint256 aaplAmount);
    event StakingFlushed(uint256 aaplAmount);
    event MinPlantEthSet(uint256 minPlantEth);
    event RakeBpsSet(uint16 bps);
    event AdminFeeBpsSet(uint16 bps);
    event JackpotBpsSet(uint16 bps);
    event JackpotOddsSet(uint32 odds);
    event SwapperSet(address swapper);
    event StakingSet(address staking);
    event RandomnessSet(address randomness);
    event KeeperSet(address keeper);

    uint8 public constant PLOTS = 25;
    uint256 public constant ROUND_SECONDS = 75;
    uint256 public constant SOW_SECONDS = 60;
    uint256 public constant MAX_PLANT_SLOTS = 250;
    uint16 public constant MAX_RAKE_BPS = 2_000;
    uint16 public constant MAX_ADMIN_FEE_BPS = 500;
    uint256 public constant MAX_MIN_PLANT_ETH = 1 ether;
    uint16 public constant STAKERS_BPS = 1_000;
    uint16 public constant JUICE_BPS = 1_000;
    uint256 private constant BPS = 10_000;
    uint256 private constant INDEX_SCALE = 1e18;
    address private constant DEAD = 0x000000000000000000000000000000000000dEaD;

    IERC20 public immutable aapl;
    IERC20 public immutable seed;
    uint256 public immutable genesis;

    ISwapper public swapper;
    IRandomness public randomness;
    IStaking public staking;
    address public keeper;
    uint256 public minPlantEth;
    uint16 public rakeBps;
    uint16 public adminFeeBps;
    uint16 public jackpotBps;
    uint32 public jackpotOdds;

    struct Round {
        uint64 sealBlock;
        bool revealed;
        bool voided;
        uint8 winningPlot;
        uint16 rakeBps;
        uint16 jackpotBps;
        uint32 jackpotOdds;
        uint256 totalStake;
        uint256 winningStake;
        uint256 netLossPool;
        uint256 entryIndex;
        uint256 jackpotWon;
    }

    mapping(uint256 => Round) public rounds;
    mapping(uint256 => mapping(uint8 => uint256)) public plotStake;
    mapping(uint256 => mapping(address => mapping(uint8 => uint256))) public stakeOf;
    mapping(uint256 => mapping(address => uint256)) public playerTotal;
    mapping(uint256 => mapping(address => bool)) public roundClaimed;
    mapping(uint256 => address[]) private roundMiners;
    mapping(uint256 => mapping(address => uint32)) public playerPlots;
    uint256 public treasuryAccrued;
    uint256 public stakingAccrued;
    uint256 public adminAccrued;
    uint256 public jackpotAccrued;
    uint256 public juiceIndex;
    uint256 public unclaimedShares;
    uint256 public nextToResolve;

    constructor(IERC20 aapl_, IERC20 seed_, ISwapper swapper_, IRandomness randomness_, uint16 rakeBps_, address owner_)
        Ownable(owner_)
    {
        if (address(aapl_) == address(0) || address(seed_) == address(0)) revert ZeroAddress();
        aapl = aapl_;
        seed = seed_;
        genesis = block.timestamp;
        _setSwapper(swapper_);
        _setRandomness(randomness_);
        _setMinPlantEth(0.0005 ether);
        _setRakeBps(rakeBps_);
        _setAdminFeeBps(100);
        _setJackpotBps(5_000);
        _setJackpotOdds(500);
    }

    function plant(uint8 plot, uint256 minAaplOut) external payable nonReentrant {
        if (plot >= PLOTS) revert InvalidPlot();
        if (msg.value < minPlantEth) revert BelowMinPlant();
        if ((block.timestamp - genesis) % ROUND_SECONDS >= SOW_SECONDS) revert SowingClosed();
        _crank(2);

        uint256 balanceBefore = aapl.balanceOf(address(this));
        swapper.swapEthForAapl{value: msg.value}(minAaplOut);
        uint256 bought = aapl.balanceOf(address(this)) - balanceBefore;
        if (bought < minAaplOut || bought == 0) revert InsufficientOutput();

        (uint256 round, uint256 stake) = _credit(plot, bought);
        emit Planted(msg.sender, round, plot, msg.value, stake);
    }

    function plantMany(uint32 plotMask, uint16 roundCount, uint256 minAaplOut) external payable nonReentrant {
        uint256 slots = _checkSpread(plotMask, roundCount);
        if (msg.value < minPlantEth * slots) revert BelowMinPlant();
        _crank(2);

        uint256 balanceBefore = aapl.balanceOf(address(this));
        swapper.swapEthForAapl{value: msg.value}(minAaplOut);
        uint256 bought = aapl.balanceOf(address(this)) - balanceBefore;
        if (bought < minAaplOut || bought == 0) revert InsufficientOutput();

        (uint256 startRound, uint256 totalStake) = _creditMany(plotMask, roundCount, slots, bought);
        emit PlantedMany(msg.sender, startRound, roundCount, plotMask, msg.value, totalStake);
    }

    function _credit(uint8 plot, uint256 bought) private returns (uint256 round, uint256 stake) {
        uint256 adminCut = (bought * adminFeeBps) / BPS;
        adminAccrued += adminCut;
        stake = bought - adminCut;
        round = currentRound();
        _stake(round, plot, stake);
    }

    function _stake(uint256 round, uint8 plot, uint256 stake) private {
        if (playerTotal[round][msg.sender] == 0) roundMiners[round].push(msg.sender);
        stakeOf[round][msg.sender][plot] += stake;
        plotStake[round][plot] += stake;
        playerTotal[round][msg.sender] += stake;
        playerPlots[round][msg.sender] |= uint32(1) << plot;
        rounds[round].totalStake += stake;
    }

    function _checkSpread(uint32 plotMask, uint16 roundCount) private pure returns (uint256 slots) {
        if (plotMask == 0 || plotMask >> PLOTS != 0) revert InvalidPlotMask();
        if (roundCount == 0) revert InvalidRoundCount();
        uint256 plots = 0;
        for (uint32 m = plotMask; m != 0; m &= m - 1) {
            plots++;
        }
        slots = plots * roundCount;
        if (slots > MAX_PLANT_SLOTS) revert TooManySlots();
    }

    function _creditMany(uint32 plotMask, uint16 roundCount, uint256 slots, uint256 bought)
        private
        returns (uint256 startRound, uint256 totalStake)
    {
        uint256 adminCut = (bought * adminFeeBps) / BPS;
        adminAccrued += adminCut;
        totalStake = bought - adminCut;
        uint256 perSlot = totalStake / slots;
        if (perSlot == 0) revert InsufficientOutput();
        startRound = currentRound();
        if ((block.timestamp - genesis) % ROUND_SECONDS >= SOW_SECONDS) startRound += 1;
        uint256 dust = totalStake - perSlot * slots;
        for (uint256 r = 0; r < roundCount; r++) {
            for (uint8 p = 0; p < PLOTS; p++) {
                if (plotMask & (uint32(1) << p) == 0) continue;
                _stake(startRound + r, p, perSlot + dust);
                dust = 0;
            }
        }
    }

    function crank(uint256 maxRounds) public {
        _crank(maxRounds);
    }

    function _crank(uint256 maxRounds) private {
        uint256 round = nextToResolve;
        uint256 limit = currentRound();
        if ((block.timestamp - genesis) % ROUND_SECONDS >= SOW_SECONDS) limit += 1;
        for (uint256 i = 0; i < maxRounds && round < limit; i++) {
            Round storage r = rounds[round];
            if (r.totalStake == 0) {
                r.voided = true;
                emit RoundVoided(round);
                round++;
                continue;
            }
            if (r.sealBlock == 0) {
                r.sealBlock = randomness.commit();
                r.rakeBps = rakeBps;
                r.jackpotBps = jackpotBps;
                r.jackpotOdds = jackpotOdds;
                emit Sealed(round, r.sealBlock);
                break;
            }
            bytes32 seed_ = randomness.seedFor(round, r.sealBlock);
            if (seed_ == bytes32(0)) {
                if (randomness.expired(r.sealBlock)) {
                    r.voided = true;
                    emit RoundVoided(round);
                    round++;
                    continue;
                }
                break;
            }
            _reveal(round, r, seed_);
            round++;
        }
        nextToResolve = round;
    }

    function _reveal(uint256 round, Round storage r, bytes32 seed_) private {
        uint8 winningPlot = uint8(uint256(seed_) % PLOTS);
        uint256 winningStake = plotStake[round][winningPlot];
        if (winningStake == 0) {
            uint256 crop = r.totalStake;
            jackpotAccrued += crop;
            r.revealed = true;
            r.winningPlot = winningPlot;
            emit Revealed(round, winningPlot, 0, 0);
            emit CropVaulted(round, winningPlot, crop);
            return;
        }
        uint256 lossPool = r.totalStake - winningStake;
        uint256 rake = (lossPool * r.rakeBps) / BPS;
        uint256 toStakers = (rake * STAKERS_BPS) / BPS;
        uint256 toJackpot = (rake * r.jackpotBps) / BPS;
        stakingAccrued += toStakers;
        jackpotAccrued += toJackpot;
        treasuryAccrued += rake - toStakers - toJackpot;

        uint256 netLossPool = lossPool - rake;
        uint256 jackpot = jackpotAccrued;
        if (jackpot != 0 && uint256(keccak256(abi.encode(seed_, "JACKPOT"))) % r.jackpotOdds == 0) {
            jackpotAccrued = 0;
            netLossPool += jackpot;
            r.jackpotWon = jackpot;
            emit JackpotHit(round, jackpot);
        }
        unclaimedShares += winningStake + netLossPool;
        r.entryIndex = juiceIndex;

        r.revealed = true;
        r.winningPlot = winningPlot;
        r.winningStake = winningStake;
        r.netLossPool = netLossPool;
        emit Revealed(round, winningPlot, winningStake, netLossPool);
    }

    function claim(uint256[] calldata roundIds) external nonReentrant {
        uint256 refunds = 0;
        uint256 grossTotal = 0;
        for (uint256 i = 0; i < roundIds.length; i++) {
            uint256 round = roundIds[i];
            Round storage r = rounds[round];
            if (!r.revealed && !r.voided) revert RoundNotResolved(round);
            if (roundClaimed[round][msg.sender]) revert AlreadyClaimed(round);
            roundClaimed[round][msg.sender] = true;
            if (r.voided) {
                refunds += playerTotal[round][msg.sender];
                continue;
            }
            uint256 winStake = stakeOf[round][msg.sender][r.winningPlot];
            if (winStake == 0) continue;
            uint256 shares = winStake + (r.netLossPool * winStake) / r.winningStake;
            grossTotal += shares + (shares * (juiceIndex - r.entryIndex)) / INDEX_SCALE;
            unclaimedShares -= shares;
        }
        uint256 fee = (grossTotal * JUICE_BPS) / BPS;
        if (fee > 0) {
            if (unclaimedShares > 0) {
                juiceIndex += (fee * INDEX_SCALE) / unclaimedShares;
            } else {
                jackpotAccrued += fee;
            }
        }
        uint256 total = refunds + grossTotal - fee;
        if (total == 0) revert NothingToClaim();
        aapl.safeTransfer(msg.sender, total);
        emit Claimed(msg.sender, total, fee);
    }

    function buybackSeed(uint256 minSeedOut) external onlyOwner nonReentrant {
        uint256 amount = treasuryAccrued;
        if (amount == 0) revert NothingToClaim();
        treasuryAccrued = 0;

        aapl.forceApprove(address(swapper), amount);
        uint256 balanceBefore = seed.balanceOf(address(this));
        swapper.swapAaplForSeed(amount, minSeedOut);
        aapl.forceApprove(address(swapper), 0);
        uint256 bought = seed.balanceOf(address(this)) - balanceBefore;
        if (bought < minSeedOut || bought == 0) revert InsufficientOutput();

        seed.safeTransfer(DEAD, bought);
        emit BuybackBurned(amount, bought);
    }

    function collectTreasury(address to) external onlyOwner nonReentrant {
        if (to == address(0)) revert ZeroAddress();
        uint256 amount = treasuryAccrued;
        if (amount == 0) revert NothingToClaim();
        treasuryAccrued = 0;
        aapl.safeTransfer(to, amount);
        emit TreasuryCollected(to, amount);
    }

    function collectAdmin(address to) external onlyOwner nonReentrant {
        if (to == address(0)) revert ZeroAddress();
        uint256 amount = adminAccrued;
        if (amount == 0) revert NothingToClaim();
        adminAccrued = 0;
        aapl.safeTransfer(to, amount);
        emit AdminCollected(to, amount);
    }

    function flushStaking() external nonReentrant {
        if (msg.sender != owner() && msg.sender != keeper) revert NotAuthorized();
        if (address(staking) == address(0)) revert ZeroAddress();
        uint256 amount = stakingAccrued;
        if (amount == 0) revert NothingToClaim();
        stakingAccrued = 0;
        aapl.forceApprove(address(staking), amount);
        uint256 balanceBefore = aapl.balanceOf(address(staking));
        staking.notifyReward(amount);
        if (aapl.balanceOf(address(staking)) - balanceBefore != amount) revert InsufficientOutput();
        emit StakingFlushed(amount);
    }

    function currentRound() public view returns (uint256) {
        return (block.timestamp - genesis) / ROUND_SECONDS;
    }

    function miners(uint256 round) external view returns (address[] memory) {
        return roundMiners[round];
    }

    function pendingClaim(address player, uint256[] calldata roundIds) external view returns (uint256 total) {
        for (uint256 i = 0; i < roundIds.length; i++) {
            uint256 round = roundIds[i];
            Round storage r = rounds[round];
            if ((!r.revealed && !r.voided) || roundClaimed[round][player]) continue;
            if (r.voided) {
                total += playerTotal[round][player];
                continue;
            }
            uint256 winStake = stakeOf[round][player][r.winningPlot];
            if (winStake == 0) continue;
            uint256 shares = winStake + (r.netLossPool * winStake) / r.winningStake;
            uint256 gross = shares + (shares * (juiceIndex - r.entryIndex)) / INDEX_SCALE;
            total += gross - (gross * JUICE_BPS) / BPS;
        }
    }

    function setMinPlantEth(uint256 minPlantEth_) external onlyOwner {
        _setMinPlantEth(minPlantEth_);
    }

    function setAdminFeeBps(uint16 bps) external onlyOwner {
        _setAdminFeeBps(bps);
    }

    function setRakeBps(uint16 bps) external onlyOwner {
        _setRakeBps(bps);
    }

    function setJackpotBps(uint16 bps) external onlyOwner {
        _setJackpotBps(bps);
    }

    function setJackpotOdds(uint32 odds) external onlyOwner {
        _setJackpotOdds(odds);
    }

    function setSwapper(ISwapper swapper_) external onlyOwner {
        _setSwapper(swapper_);
    }

    function setStaking(IStaking staking_) external onlyOwner {
        if (address(staking_) == address(0)) revert ZeroAddress();
        staking = staking_;
        emit StakingSet(address(staking_));
    }

    function setRandomness(IRandomness randomness_) external onlyOwner {
        _setRandomness(randomness_);
    }

    function setKeeper(address keeper_) external onlyOwner {
        keeper = keeper_;
        emit KeeperSet(keeper_);
    }

    function renounceOwnership() public view override onlyOwner {
        revert RenounceDisabled();
    }

    function _setMinPlantEth(uint256 minPlantEth_) private {
        if (minPlantEth_ > MAX_MIN_PLANT_ETH) revert MinPlantTooHigh();
        minPlantEth = minPlantEth_;
        emit MinPlantEthSet(minPlantEth_);
    }

    function _setRakeBps(uint16 bps) private {
        if (bps > MAX_RAKE_BPS) revert BpsTooHigh();
        rakeBps = bps;
        emit RakeBpsSet(bps);
    }

    function _setAdminFeeBps(uint16 bps) private {
        if (bps > MAX_ADMIN_FEE_BPS) revert BpsTooHigh();
        adminFeeBps = bps;
        emit AdminFeeBpsSet(bps);
    }

    function _setJackpotBps(uint16 bps) private {
        if (bps > BPS - STAKERS_BPS) revert BpsTooHigh();
        jackpotBps = bps;
        emit JackpotBpsSet(bps);
    }

    function _setJackpotOdds(uint32 odds) private {
        if (odds == 0) revert ZeroOdds();
        jackpotOdds = odds;
        emit JackpotOddsSet(odds);
    }

    function _setSwapper(ISwapper swapper_) private {
        if (address(swapper_) == address(0)) revert ZeroAddress();
        swapper = swapper_;
        emit SwapperSet(address(swapper_));
    }

    function _setRandomness(IRandomness randomness_) private {
        if (address(randomness_) == address(0)) revert ZeroAddress();
        randomness = randomness_;
        emit RandomnessSet(address(randomness_));
    }
}
Chain explorer4153msChain node76ms