Main
0x5f623dcf224ea704d7cc840b37752f30efd8f110
Verification
Verified
v0.8.27+commit.40a35a09
Type
Contract
11,254 bytes
ABI entries
94
27 read · 24 write
License
mit
Contract information
- Address
- 0x5f623dcf224ea704d7cc840b37752f30efd8f110
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.27+commit.40a35a09
- Optimization
- Enabled
- Creator
- 0xC43b981C89…8bFa9b97Da
- Creation tx
- 0xe0eda03aef…626b4558e4
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (27)
STARTER_FACILITY_INDEX() → uint256
STARTER_MINER_INDEX() → uint256
acquiredStarterMiner(address) → bool
burnPct() → uint256
cash() → address
cooldown() → uint256
facilities(uint256) → uint256, uint256, uint256, bool, uint256, uint256
facilityCount() → uint256
getPlayerMinersPaginated(address, uint256, uint256) → tuple[]
getReferrals(address) → address[]
initializedStarterFacility(address) → bool
lastFacilityUpgradeTimestamp(address) → uint256
lottery() → address
minerSecondHandMarket(uint256) → uint256
miners(uint256) → uint256, uint256, uint256, uint256, uint256, uint256, uint256, bool
owner() → address
ownerToFacility(address) → uint256, uint256, uint256, uint256, uint256, uint256, uint256
playerHashrate(address) → uint256
playerMinersId(uint256) → uint256, uint256, uint256, uint256, uint256, uint256, uint256, bool
playerMinersOwned(address) → uint256
playerOccupiedCoords(address, uint256, uint256) → bool
referrals(address) → address
referredUsers(address, uint256) → address
robbersCorporation() → address
timeUntilNextFacilityUpgrade(address) → uint256
totalHashrate() → uint256
uniqueMinerCount() → uint256
Events (15)
FacilityBoughtFacilityCostChangedFacilityProductionToggledInitialFacilityPurchasedLotteryUpdatedMinerBoughtMinerCostChangedMinerProductionToggledMinerSecondaryMarketAddedMinerSoldNewFacilityAddedNewMinerAddedOwnershipTransferredPlayerHashrateDecreasedPlayerHashrateIncreased
ABI
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyAtMaxFacility",
"type": "error"
},
{
"inputs": [],
"name": "AlreadyPurchasedInitialFactory",
"type": "error"
},
{
"inputs": [],
"name": "CantBuyNewFacilityYet",
"type": "error"
},
{
"inputs": [],
"name": "CantModifyStarterFacility",
"type": "error"
},
{
"inputs": [],
"name": "CantModifyStarterMiner",
"type": "error"
},
{
"inputs": [],
"name": "CorporationNotSet",
"type": "error"
},
{
"inputs": [],
"name": "FacilityDimensionsInvalid",
"type": "error"
},
{
"inputs": [],
"name": "FacilityInadequatePowerOutput",
"type": "error"
},
{
"inputs": [],
"name": "GreatDepression",
"type": "error"
},
{
"inputs": [],
"name": "IndexOutOfBounds",
"type": "error"
},
{
"inputs": [],
"name": "InvalidFacilityIndex",
"type": "error"
},
{
"inputs": [],
"name": "InvalidFee",
"type": "error"
},
{
"inputs": [],
"name": "InvalidMinerCoordinates",
"type": "error"
},
{
"inputs": [],
"name": "InvalidMinerIndex",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPowerOutput",
"type": "error"
},
{
"inputs": [],
"name": "InvalidReferrer",
"type": "error"
},
{
"inputs": [],
"name": "MinerNotInProduction",
"type": "error"
},
{
"inputs": [],
"name": "NeedToInitializeFacility",
"type": "error"
},
{
"inputs": [],
"name": "NewFacilityNotInProduction",
"type": "error"
},
{
"inputs": [],
"name": "NonExistentFacility",
"type": "error"
},
{
"inputs": [],
"name": "NonExistentMiner",
"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": "PlayerDoesNotOwnMiner",
"type": "error"
},
{
"inputs": [],
"name": "StarterMinerAlreadyAcquired",
"type": "error"
},
{
"inputs": [],
"name": "TooPoor",
"type": "error"
},
{
"inputs": [],
"name": "WithdrawFailed",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "player",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "cost",
"type": "uint256"
}
],
"name": "FacilityBought",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newCost",
"type": "uint256"
}
],
"name": "FacilityCostChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"name": "FacilityProductionToggled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "player",
"type": "address"
}
],
"name": "InitialFacilityPurchased",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldLottery",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newLottery",
"type": "address"
}
],
"name": "LotteryUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "payer",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "player",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "minerId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"name": "MinerBought",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newCost",
"type": "uint256"
}
],
"name": "MinerCostChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"name": "MinerProductionToggled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "price",
"type": "uint256"
}
],
"name": "MinerSecondaryMarketAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "player",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "secondHandPrice",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "minerId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"name": "MinerSold",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "totalPowerOutput",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "inProduction",
"type": "bool"
},
{
"indexed": false,
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"name": "NewFacilityAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "hashRate",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "powerConsumption",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"name": "NewMinerAdded",
"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": false,
"internalType": "uint256",
"name": "playerHashrate",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "playerPendingRewards",
"type": "uint256"
}
],
"name": "PlayerHashrateDecreased",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "player",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "playerHashrate",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "playerPendingRewards",
"type": "uint256"
}
],
"name": "PlayerHashrateIncreased",
"type": "event"
},
{
"inputs": [],
"name": "STARTER_FACILITY_INDEX",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "STARTER_MINER_INDEX",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "acquiredStarterMiner",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maxMiners",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalPowerOutput",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"name": "addFacility",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "hashrate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "powerConsumption",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"name": "addMiner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "price",
"type": "uint256"
}
],
"name": "addSecondaryMarketForMiner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "burnPct",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"name": "buyMiner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
},
{
"internalType": "address",
"name": "_receiver",
"type": "address"
}
],
"name": "buyMinerFor",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "buyNewFacility",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "cash",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "newCost",
"type": "uint256"
}
],
"name": "changeFacilityCost",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "newCost",
"type": "uint256"
}
],
"name": "changeMinerCost",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "cooldown",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "facilities",
"outputs": [
{
"internalType": "uint256",
"name": "maxMiners",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalPowerOutput",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "facilityCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"name": "getFreeStarterMiner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "player",
"type": "address"
},
{
"internalType": "uint256",
"name": "startIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "size",
"type": "uint256"
}
],
"name": "getPlayerMinersPaginated",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "hashrate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "powerConsumption",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"internalType": "struct Miner[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "referrer",
"type": "address"
}
],
"name": "getReferrals",
"outputs": [
{
"internalType": "address[]",
"name": "",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "governanceRecoverUnsupported",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "initializedStarterFacility",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "lastFacilityUpgradeTimestamp",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lottery",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "minerSecondHandMarket",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "miners",
"outputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "hashrate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "powerConsumption",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "ownerToFacility",
"outputs": [
{
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxMiners",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "currMiners",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalPowerOutput",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "currPowerOutput",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "playerHashrate",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "playerMinersId",
"outputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "y",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "hashrate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "powerConsumption",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cost",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "playerMinersOwned",
"outputs": [
{
"internalType": "uint256",
"name": "_spacer",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "playerOccupiedCoords",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "referrer",
"type": "address"
}
],
"name": "purchaseInitialFacility",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "referrals",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "referredUsers",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "robbersCorporation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minerId",
"type": "uint256"
}
],
"name": "sellMiner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "burn",
"type": "uint256"
}
],
"name": "setBurnPct",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_cash",
"type": "address"
}
],
"name": "setCash",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_robbersCorporation",
"type": "address"
}
],
"name": "setCashersCorporation",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_cooldown",
"type": "uint256"
}
],
"name": "setCooldown",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_lottery",
"type": "address"
}
],
"name": "setLottery",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "player",
"type": "address"
}
],
"name": "syncPlayerHashrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "player",
"type": "address"
}
],
"name": "timeUntilNextFacilityUpgrade",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "facilityIndex",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"name": "toggleFacilityProduction",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minerIndex",
"type": "uint256"
},
{
"internalType": "bool",
"name": "inProduction",
"type": "bool"
}
],
"name": "toggleMinerProduction",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "totalHashrate",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "uniqueMinerCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amt",
"type": "uint256"
}
],
"name": "withdrawCash",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Ownable} from "./openzeppelin/access/Ownable.sol";
import {EnumerableSetLib} from "./solady/utils/EnumerableSetLib.sol";
import {FixedPointMathLib} from "./solady/utils/FixedPointMathLib.sol";
import {ICash} from "./interfaces/ICash.sol";
import {ILotteryHashrate} from "./interfaces/ILotteryHashrate.sol";
import {Miner} from "./types/Miner.sol";
import {Facility} from "./types/Facility.sol";
import {NewFacility} from "./types/NewFacility.sol";
import {Errors} from "./libraries/Errors.sol";
import {Events} from "./libraries/Events.sol";
// Cash main program
contract Main is Ownable {
using EnumerableSetLib for EnumerableSetLib.Uint256Set;
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// STORAGE
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/// @dev Cash token address.
address public cash;
/// @dev robbersCorporation fee recipient.
address public robbersCorporation;
/// @dev CASH lottery that receives exact player hashrate updates.
address public lottery;
// every miner has an id
uint256 universalMinerId;
/// @dev Total unique miners.
uint256 public uniqueMinerCount;
/// @dev Total facilities.
uint256 public facilityCount;
/// @dev Total network hashrate.
uint256 public totalHashrate;
/// @dev Initial cooldown time is 24H.
uint256 public cooldown = 24 hours;
/// @dev Initial burn is 75%.
uint256 public burnPct = 0.75e18;
/// @dev Players total hashrate.
mapping(address => uint256) public playerHashrate;
/// @dev Tracks different miners and its stats.
mapping(uint256 => Miner) public miners;
/// @dev Tracks different facilities and its stats.
mapping(uint256 => NewFacility) public facilities;
/// @dev Tracks how many of each miner a player has. Only tracks Id.
mapping(address => EnumerableSetLib.Uint256Set) public playerMinersOwned;
/// @dev Maps minerId to the actual miner struct
mapping(uint256 => Miner) public playerMinersId;
/// @dev Tracks players facility stats.
mapping(address => Facility) public ownerToFacility;
/// @dev Tracks new players initializing in the game.
mapping(address => bool) public initializedStarterFacility;
/// @dev Tracks if a player has acquired a free miner.
mapping(address => bool) public acquiredStarterMiner;
/// @dev Tracks if a there's a secondary market for a certain miner.
mapping(uint256 => uint256) public minerSecondHandMarket;
/// @dev Tracks each miners coords for a player
mapping(address => mapping(uint256 => mapping(uint256 => bool))) public playerOccupiedCoords;
mapping(address => uint256) public lastFacilityUpgradeTimestamp;
/// @dev Tracks referrals.
mapping(address => address) public referrals;
/// @dev Tracks referred users.
mapping(address => address[]) public referredUsers;
/// @dev start miner and facility index (these will never change since every player starts with same facility).
uint256 public immutable STARTER_MINER_INDEX;
uint256 public immutable STARTER_FACILITY_INDEX;
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// CONSTRUCTOR
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
constructor() Ownable(msg.sender) {
STARTER_MINER_INDEX = ++uniqueMinerCount;
// add starter free miner
miners[STARTER_MINER_INDEX] = Miner(
STARTER_MINER_INDEX,
type(uint256).max,
type(uint256).max,
type(uint256).max,
100,
1,
type(uint256).max, // this is the free starter miner, people cannot buy it
false
);
// add starter facility
facilities[++facilityCount] = NewFacility(
4,
28,
type(uint256).max, // starter facility, people cannot buy it
false,
2,
2
);
STARTER_FACILITY_INDEX = facilityCount;
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// USER FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev All new players claim the initial facility for free.
* Players can only purchase upgrades after claiming the initial facility.
*/
function purchaseInitialFacility(address referrer) external {
if (initializedStarterFacility[msg.sender]) {
revert Errors.AlreadyPurchasedInitialFactory();
}
if (referrer == msg.sender) {
revert Errors.InvalidReferrer();
}
initializedStarterFacility[msg.sender] = true;
if (referrer != address(0)) {
referrals[msg.sender] = referrer;
referredUsers[referrer].push(msg.sender);
}
NewFacility memory newFacility = facilities[STARTER_FACILITY_INDEX];
Facility storage facility = ownerToFacility[msg.sender];
// initialize players starter facility
facility.facilityIndex = STARTER_FACILITY_INDEX;
facility.maxMiners = newFacility.maxMiners;
facility.totalPowerOutput = newFacility.totalPowerOutput;
facility.x = newFacility.x;
facility.y = newFacility.y;
emit Events.InitialFacilityPurchased(msg.sender);
}
/**
* @dev All new players can redeem 1 free miner.
*
* Players are not required to redeem a free miner.
*/
function getFreeStarterMiner(uint256 x, uint256 y) external {
if (acquiredStarterMiner[msg.sender]) {
revert Errors.StarterMinerAlreadyAcquired();
}
acquiredStarterMiner[msg.sender] = true;
Miner memory miner = miners[STARTER_MINER_INDEX];
Facility storage facility = ownerToFacility[msg.sender];
if (_isInvalidCoordinates(x, y, facility.x, facility.y)) {
revert Errors.InvalidMinerCoordinates();
}
if (facility.currPowerOutput + miner.powerConsumption > facility.totalPowerOutput) {
revert Errors.FacilityInadequatePowerOutput();
}
miner.x = x;
miner.y = y;
miner.id = ++universalMinerId;
playerOccupiedCoords[msg.sender][x][y] = true;
playerMinersOwned[msg.sender].add(universalMinerId);
playerMinersId[universalMinerId] = miner;
// increase facility miners
facility.currMiners++;
// increase power consumption from factory
facility.currPowerOutput += miner.powerConsumption;
// emit Events.FreeMinerRedeemed(msg.sender);
emit Events.MinerBought(msg.sender, msg.sender, STARTER_MINER_INDEX, 0, universalMinerId, x, y);
_increaseHashrate(msg.sender, miner.hashrate);
}
function buyMiner(uint256 minerIndex, uint256 x, uint256 y) external {
_buyMiner(minerIndex, x, y, msg.sender);
}
function buyMinerFor(uint256 minerIndex, uint256 x, uint256 y, address _receiver) external {
_buyMiner(minerIndex, x, y, _receiver);
}
/**
* @dev Purchase new miners using cash.
*/
function _buyMiner(uint256 minerIndex, uint256 x, uint256 y, address _receiver) internal {
// check user has enough funds for miner and facility allows it
Miner memory miner = miners[minerIndex];
Facility storage facility = ownerToFacility[_receiver];
if (_isInvalidCoordinates(x, y, facility.x, facility.y)) {
revert Errors.InvalidMinerCoordinates();
}
// this case will cover if an index too large is passed in
if (!miner.inProduction) revert Errors.MinerNotInProduction();
if (ICash(cash).balanceOf(msg.sender) < miner.cost) {
revert Errors.TooPoor();
}
if (facility.currPowerOutput + miner.powerConsumption > facility.totalPowerOutput) {
revert Errors.FacilityInadequatePowerOutput();
}
// Transfer the price, burn the configured share, and route the remainder immediately.
ICash(cash).transferFrom(msg.sender, address(this), miner.cost);
_splitPurchasePayment(miner.cost);
miner.x = x;
miner.y = y;
miner.id = ++universalMinerId;
playerOccupiedCoords[_receiver][x][y] = true;
playerMinersOwned[_receiver].add(universalMinerId);
playerMinersId[universalMinerId] = miner;
// increase facility miners
facility.currMiners++;
// increase power consumption from factory
facility.currPowerOutput += miner.powerConsumption;
emit Events.MinerBought(msg.sender, _receiver, minerIndex, miner.cost, universalMinerId, x, y);
_increaseHashrate(_receiver, miner.hashrate);
}
/**
* @dev Sell miners, if there's a secondary market, player will get cash in return.
*
* Players will sell miners in the case they run out of spots in their facility to put new miners.
*/
function sellMiner(uint256 minerId) external {
if (!playerMinersOwned[msg.sender].contains(minerId)) {
revert Errors.PlayerDoesNotOwnMiner();
}
Miner memory miner = playerMinersId[minerId];
uint256 secondHandPrice = minerSecondHandMarket[miner.minerIndex];
// if there's not enough cash in this contract to pay out, revert
if (secondHandPrice > ICash(cash).balanceOf(address(this))) {
revert Errors.GreatDepression();
}
Facility storage facility = ownerToFacility[msg.sender];
// decrease facility miners
facility.currMiners--;
// decrease power consumption from factory
facility.currPowerOutput -= miner.powerConsumption;
playerMinersOwned[msg.sender].remove(minerId);
delete playerMinersId[minerId];
playerOccupiedCoords[msg.sender][miner.x][miner.y] = false;
emit Events.MinerSold(msg.sender, miner.minerIndex, secondHandPrice, minerId, miner.x, miner.y);
_decreaseHashrate(msg.sender, miner.hashrate);
if (secondHandPrice > 0) {
ICash(cash).transfer(msg.sender, secondHandPrice);
}
}
/**
* @dev Purchase larger or more power output facility.
*
* Players must linearly climb the facilities.
*/
function buyNewFacility() external {
// need to purchase initial facility first
if (!initializedStarterFacility[msg.sender]) {
revert Errors.NeedToInitializeFacility();
}
Facility storage currFacility = ownerToFacility[msg.sender];
uint256 currFacilityIndex = currFacility.facilityIndex;
if (currFacilityIndex == facilityCount) {
revert Errors.AlreadyAtMaxFacility();
}
// 24H cooldown between each facility upgrade
if (block.timestamp - lastFacilityUpgradeTimestamp[msg.sender] < cooldown) {
revert Errors.CantBuyNewFacilityYet();
}
NewFacility memory newFacility = facilities[currFacilityIndex + 1];
if (!newFacility.inProduction) {
revert Errors.NewFacilityNotInProduction();
}
if (ICash(cash).balanceOf(msg.sender) < newFacility.cost) {
revert Errors.TooPoor();
}
ICash(cash).transferFrom(msg.sender, address(this), newFacility.cost);
_splitPurchasePayment(newFacility.cost);
currFacility.facilityIndex++;
currFacility.maxMiners = newFacility.maxMiners;
currFacility.totalPowerOutput = newFacility.totalPowerOutput;
currFacility.x = newFacility.x;
currFacility.y = newFacility.y;
lastFacilityUpgradeTimestamp[msg.sender] = block.timestamp;
emit Events.FacilityBought(msg.sender, currFacility.facilityIndex, newFacility.cost);
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// INTERNAL FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev Increase players hashrate.
*/
function _increaseHashrate(address player, uint256 hashrate) internal {
totalHashrate += hashrate;
playerHashrate[player] += hashrate;
_syncPlayerHashrate(player);
emit Events.PlayerHashrateIncreased(player, playerHashrate[player], 0);
}
/**
* @dev Decrease players hashrate.
*/
function _decreaseHashrate(address player, uint256 hashrate) internal {
totalHashrate -= hashrate;
playerHashrate[player] -= hashrate;
_syncPlayerHashrate(player);
emit Events.PlayerHashrateDecreased(player, playerHashrate[player], 0);
}
function _syncPlayerHashrate(address player) internal {
if (lottery != address(0)) {
ILotteryHashrate(lottery).setPlayerHashrate(player, playerHashrate[player]);
}
}
function _splitPurchasePayment(uint256 amount) internal {
uint256 burned = FixedPointMathLib.mulWad(amount, burnPct);
if (burned > 0) ICash(cash).burn(burned);
uint256 corporationShare = amount - burned;
if (corporationShare > 0) {
if (robbersCorporation == address(0)) revert Errors.CorporationNotSet();
ICash(cash).transfer(robbersCorporation, corporationShare);
}
}
/**
* @dev checks the miners x,y coords are within bounds and there isn't an existent miner at that position.
*/
function _isInvalidCoordinates(uint256 desiredX, uint256 desiredY, uint256 facilityX, uint256 facilityY)
internal
view
returns (bool)
{
if (desiredX >= facilityX || desiredY >= facilityY) {
return true;
}
return playerOccupiedCoords[msg.sender][desiredX][desiredY];
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// VIEW FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev Get next available facility upgrade time.
*/
function timeUntilNextFacilityUpgrade(address player) external view returns (uint256) {
if (lastFacilityUpgradeTimestamp[player] + cooldown < block.timestamp) {
return 0;
}
return (lastFacilityUpgradeTimestamp[player] + cooldown) - block.timestamp;
}
/**
* @dev Get all miners for a player.
*/
function getPlayerMinersPaginated(address player, uint256 startIndex, uint256 size)
external
view
returns (Miner[] memory)
{
EnumerableSetLib.Uint256Set storage set = playerMinersOwned[player];
uint256 length = set.length();
// Return empty array if start index is beyond array bounds
if (startIndex >= length) {
return new Miner[](0);
}
// Calculate how many items we can actually return
uint256 remaining = length - startIndex;
uint256 returnSize = size > remaining ? remaining : size;
Miner[] memory playerMiners = new Miner[](returnSize);
for (uint256 i = 0; i < returnSize; i++) {
playerMiners[i] = playerMinersId[set.at(startIndex + i)];
}
return playerMiners;
}
/**
* @dev Get all referred users.
*/
function getReferrals(address referrer) external view returns (address[] memory) {
return referredUsers[referrer];
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// OWNER FUNCTIONS
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* @dev Add new miner to game.
*/
function addMiner(uint256 hashrate, uint256 powerConsumption, uint256 cost, bool inProduction) external onlyOwner {
// must increment first cause compiler evaluates function arg before evaluating the assignment
++uniqueMinerCount;
miners[uniqueMinerCount] = Miner(uniqueMinerCount, 0, 0, 0, hashrate, powerConsumption, cost, inProduction);
emit Events.NewMinerAdded(uniqueMinerCount, hashrate, powerConsumption, cost, inProduction);
}
/**
* @dev Allows or prevents miner from being purchased.
*
* Miners cannot be removed, only toggled off from purchased.
*/
function toggleMinerProduction(uint256 minerIndex, bool inProduction) external onlyOwner {
if (minerIndex < STARTER_MINER_INDEX || minerIndex > uniqueMinerCount) {
revert Errors.InvalidMinerIndex();
}
Miner storage miner = miners[minerIndex];
miner.inProduction = inProduction;
emit Events.MinerProductionToggled(minerIndex, inProduction);
}
/**
* @dev Add new facility to game.
*/
function addFacility(
uint256 maxMiners,
uint256 totalPowerOutput,
uint256 cost,
bool inProduction,
uint256 x,
uint256 y
) external onlyOwner {
if (x * y != maxMiners) {
revert Errors.FacilityDimensionsInvalid();
}
if (facilities[facilityCount].x > x || facilities[facilityCount].y > y) {
revert Errors.FacilityDimensionsInvalid();
}
if (facilities[facilityCount].totalPowerOutput > totalPowerOutput) {
revert Errors.InvalidPowerOutput();
}
facilities[++facilityCount] = NewFacility(maxMiners, totalPowerOutput, cost, inProduction, x, y);
emit Events.NewFacilityAdded(facilityCount, totalPowerOutput, cost, inProduction, x, y);
}
/**
* @dev Allows or prevents new facilities from being purchased.
*
* New facilities cannot be removed, only toggled off from purchased.
*/
function toggleFacilityProduction(uint256 facilityIndex, bool inProduction) external onlyOwner {
if (facilityIndex < STARTER_FACILITY_INDEX || facilityIndex > facilityCount) {
revert Errors.InvalidFacilityIndex();
}
NewFacility storage facility = facilities[facilityIndex];
facility.inProduction = inProduction;
emit Events.FacilityProductionToggled(facilityIndex, inProduction);
}
/**
* @dev Add secondary market for a miner.
*/
function addSecondaryMarketForMiner(uint256 minerIndex, uint256 price) external onlyOwner {
minerSecondHandMarket[minerIndex] = price;
emit Events.MinerSecondaryMarketAdded(minerIndex, price);
}
function setCash(address _cash) external onlyOwner {
cash = _cash;
}
function setLottery(address _lottery) external onlyOwner {
address oldLottery = lottery;
lottery = _lottery;
emit Events.LotteryUpdated(oldLottery, _lottery);
}
/// @notice Re-publish a player's current hashrate after a lottery migration.
function syncPlayerHashrate(address player) external {
_syncPlayerHashrate(player);
}
function setCashersCorporation(address _robbersCorporation) external onlyOwner {
robbersCorporation = _robbersCorporation;
}
function setBurnPct(uint256 burn) external onlyOwner {
if (burn > 1e18) revert Errors.InvalidFee();
burnPct = burn;
}
function setCooldown(uint256 _cooldown) external onlyOwner {
cooldown = _cooldown;
}
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
(bool success,) = payable(robbersCorporation).call{value: balance}("");
if (!success) revert Errors.WithdrawFailed();
}
function withdrawCash(uint256 amt) external onlyOwner {
ICash(cash).transfer(robbersCorporation, amt);
}
function changeMinerCost(uint256 minerIndex, uint256 newCost) external onlyOwner {
if (minerIndex > uniqueMinerCount) {
revert Errors.NonExistentMiner();
}
if (minerIndex == STARTER_MINER_INDEX) {
revert Errors.CantModifyStarterMiner();
}
Miner storage miner = miners[minerIndex];
miner.cost = newCost;
emit Events.MinerCostChanged(minerIndex, newCost);
}
function changeFacilityCost(uint256 facilityIndex, uint256 newCost) external onlyOwner {
if (facilityIndex > facilityCount) {
revert Errors.NonExistentFacility();
}
if (facilityIndex == STARTER_FACILITY_INDEX) {
revert Errors.CantModifyStarterFacility();
}
NewFacility storage facility = facilities[facilityIndex];
facility.cost = newCost;
emit Events.FacilityCostChanged(facilityIndex, newCost);
}
function governanceRecoverUnsupported(address _token, uint256 amount, address to) external onlyOwner {
ICash(_token).transfer(to, amount);
}
}
Chain explorer4790msChain node82ms