The Citadel

Smart Contract Audit Report

Audit Summary

The Citadel is an expansive new game where users explore space and mine Ore while playing as Marauders or Miners.

For this audit, we reviewed the contracts folder at commit bc0a02a32b2f24b3d354b1c7a9d26c2018c906fd on the team's private GitHub.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: February 11th, 2022.
Updated: February 25th, 2022 to address users being able to send too much ETH when minting and various additions to functionality.
Updated: April 26th, 2022 to address updates at commit e768d86700f3149e86e8431cf51bd6bfaaf3c281.
Updated: April 29th, 2022 to address changes from commit e768d86700f3149e86e8431cf51bd6bfaaf3c281 to commit 142e3a502241918543643e51799d72f1531ebec7.
Updated: May 11th, 2022 to address updates at commit bc0a02a32b2f24b3d354b1c7a9d26c2018c906fd including bytes32 updates and robust documentation.

Finding #1 - FixedPriceShips - Informational (Resolved)

Description: Extra ETH used to mint ships is not returned to the user.
Risk/Impact: Users may mint ships at a higher cost than they should and lose the extra ETH sent.
Recommendation: Require that the user sends the exact amount of ETH required. This can also be implemented in the DutchAuctionShips contract.
Resolution: The team has implemented the above recommendation.

Contracts Overview

  • The team worked with us to implement various gas optimizations throughout the contracts.
  • The contracts are implemented using the Authorizable permission scheme.
  • This allows the contract owner to set various "roles" within the contract with each role having potentially different permissions.
  • The deployer is given the Default Admin role upon deployment.
  • The Default Admin may assign any role to any address at any time.
  • The contracts are written in Solidity version 0.8.X so they are protected from underflow/overflow.
  • The contracts utilize ReentrancyGuard where appropriate.
BaseShips Contract:
  • Ships are represented by an ERC721 NFT; Ships are used to traverse the universe and collect Ore.
  • The contract is ERC721 compliant, all standard functionality is present.
  • Users may burn any of their Ships at any time.
  • Users may set and update a Ship's name for a "naming fee" in Ore burned from the user; names are limited to 32 alphanumeric characters.
  • The Vulgarity Admin role may add any name to the "banned list" at any time. Users may not set their Ship's name to any name in the banned list.
  • Any user may trigger a withdrawal from the contract. This will transfer the Governance, Team 1, and Team 2 addresses their relative portion of the current funds in the contract.
  • When ProxyRegistry is enabled, users may set a Proxy address allowing them to transfer Ships on the user's behalf.
  • NFT metadata that contains information about the Ships associated graphic is stored using an off-chain URI endpoint.
  • The deployer is granted the Default Admin, Supply Admin, Sale Admin, and Registry Admin roles upon deployment.
  • The Supply Admin is given maximum allowance for all Ships, allowing them to transfer, burn, and grant approval for Ships on users' behalf.
  • The Supply Admin may "issue" any number of Ships to any address at any time. They may optionally include a name for the Ship when issued.
  • The Supply Admin may update any Ship at any time, changing the name and/or setting the Ship ID to a different set of traits.
  • The Registry Admin may enable and disable the ability to use the Proxy Registry at any time.
  • The Sale Admin may update the Merkle root used for verification at any time.
FixedPriceShips Contract:
  • This contract allows the project team to offer users the ability to mint Ships at a fixed price.
  • The contract is built on top of the BaseShips contract inheriting all of its functionality and permission scheme.
  • The minting cost, the maximum Ships per public mint, and the maximum total number of Ships that can be minted through public minting and whitelist minting are declared upon deployment.
  • When public minting is enabled, any user may mint up to the maximum number of Ships per public mint while the total supply remains under the maximum total publicly mintable Ships.
  • When whitelist minting is enabled, any whitelisted user may mint while the total supply remains under the maximum total whitelist mintable Ships.
  • There is no explicit whitelist in the contract, rather users provide "proof" that they are a whitelisted user.
  • The proof is verified with a Merkle tree that the team has generated off-chain containing the whitelisted users and the number of ships they may mint.
  • The naming fee for FixedPriceShips is 1000 Ore.
  • The Governance percentage for FixedPriceShips is 70%, with the remaining 30% split evenly between the Team 1 and Team 2 addresses.
  • The Sale Admin may toggle public and whitelist minting at any time.
DutchAuctionShips
  • This contract is used in conjunction with the ConstructionBay contract to offer users the ability to purchase Ships through a Dutch Auction contract.
  • The contract is built on top of the BaseShips contract inheriting all of its functionality and permission scheme.
  • The maximum Ships per public mint, and the maximum total number of Ships that can be minted through public minting and whitelist minting are declared upon deployment.
  • Ships have an auction start time, duration, and starting price; the sale can also be set to active or inactive.
  • Over the course of the auction, the starting price will decrease by the "decrease amount" over the course of the "decrease interval" until a winning bid is accepted or the decrease duration has passed and the price reaches the price floor.
  • When the sale is active, any user may mint up to the maximum number of Ships per public mint while the total supply remains under the maximum total publicly mintable Ships.
  • When the sale is active, any whitelisted user may mint Ships for half the sale price, while the total supply remains under the maximum total whitelist mintable Ships.
  • There is no explicit whitelist in the contract, rather users provide a "proof" that they are a whitelisted user.
  • The proof is verified with a Merkle tree that the team has generated off-chain containing the whitelisted users and the number of ships they may mint.
  • The naming fee for DutchAuctionShips is 1000 Ore.
  • The Governance percentage for DutchAuctionShips is 70%, with the remaining 30% split evenly between the Team 1 and Team 2 addresses.
  • The Sale Admin may update the sale information at any time.
  • The Sale Admin may set the sale as active or inactive at any time.
BaseShipTraits Contract:
  • This contract is used to generate a Ship's underlying traits for a specified range of Ship IDs.
  • All Ships share 3 base traits: speed, fuel efficiency, and power.
  • Speed and fuel efficiency affect a Ship's ability to travel whereas power affects its ability to collect Ore; the higher the trait the better the performance.
  • Each Ship also has a designation and class which will affect the base traits of the Ship.
  • The classes are split into small, medium, and large whereas the designations are split into Standard, Noble, and Exodus.
  • The smaller a Ship is, the less power it will have, but in return it will have greater speed and fuel efficiency. The larger a Ship is the more power it will have, but will suffer from less speed and fuel efficiency.
  • A Ship's designation also affects its base traits; generally, Standard Ships will have lower traits than a Noble Ship of the same size. The same is true for Noble and Exodus Ships, respectively.
  • Finally, a Ship has one of two careers: Miner or Marauder. As the name suggests, Miners generate Ore through mining, whereas Marauders tax Ore from Miners based on Belt conditions.
  • Users must make an initial call to begin the generation of a Ship's seed.
  • A Ship's seed is determined by a random number generated through a Chainlink VRF; this is the industry standard and is resistant to manipulation.
  • A separate call to reveal the Ship's traits from the seed is needed after a Ship's seed has been set.
FixedShipTraits Contract:
  • This contract allows the team to supply a set of fixed predetermined traits to a specified number of Ships.
  • The contract is built on top of the BaseShipTraits contract inheriting all of its functionality and permission scheme.
  • The Ships Uploader role may upload any number of Ships while the upload has not been marked complete.
  • Once at least the specified number of Ships has been uploaded, the Ships Uploader may mark the upload complete at any time.
  • When a user makes a call to reveal a number of Ships' traits, an equal number of randomly generated seeds is supplied from the BaseShipTraits contract.
  • Each seed is then used to select one of the supplied Ship traits; the selected Ship traits are removed from the pool of available traits.
  • The deployer is granted the Ships Uploader and Default Admin role upon deployment.
GenesisShipTraits Contract:
  • This contract is used to randomly generate Ship traits from a seed based on predefined probabilities.
  • Ship traits generated through GenesisShipTraits have a small chance to be a 4th Designation known as Officer. These ships have their unique traits supplied by the team upon deployment.
  • The contract is built on top of the BaseShipTraits contract inheriting all of its functionality and permission scheme.
  • When a user makes a call to reveal a number of Ships' traits, an equal number of randomly generated seeds is supplied from the BaseShipTraits contract.
  • The seeds are then used to generate the trait values for the Ships using the Alias Method.
  • Users get a first initial roll at any possible result for the trait. If the first roll fails, users are assigned a result from a second roll which may contain all possible results or be strictly lower rolls.
  • Ships that roll a Standard designation have an additional roll for a chance at upgraded stats.
Ore Contract:
  • Ore is earned through mining or marauding and used to travel and upgrade Ships.
  • Users may burn any of their Ore at any time.
  • The contract is ERC20 compliant, all standard functionality is present.
  • The deployer is granted the Default Admin and Supply Admin role on deployment.
  • The Supply Admin may mint any amount of tokens to any address at any time.
PilotBadge Contract:
  • PilotBadge tokens are minted to users when they stake a Ship at The Citadel and burned when a Ship is unstaked.
  • Only the Supply Admin may transfer tokens.
  • The Supply Admin is granted maximum allowance for all tokens allowing them to transfer and burn any users' tokens at any time.
  • Each PilotBadge token additionally represents votes intended to be used in a DAO where one token represents one vote.
  • Users must delegate to themselves to exercise their voting rights.
  • Users may delegate their votes to another address allowing them to vote on behalf of the user.
  • Users must explicitly delegate back to themselves to regain their votes if they have been delegated.
  • Users also have the option to delegate through the use of a signed message, allowing for a gasless delegation for the user.
Travel Contract:
  • The Citadel universe exists over an (x,y) plane where The Citadel itself is at point (0,0).
  • Users may travel to any valid destination, moving in a straight line from their current position to the destination position.
  • When a user begins traveling, Ore is burned based on the Ship's fuel efficiency and the distance between its current position and the destination position.
  • When a Ship is traveling, its location is not automatically updated. The Ship's location is only updated when a request for its location is made.
  • The current location is then calculated from the Ship's speed and the time elapsed between when travel was initiated and the current time.
  • In the case that a Ship was traveling longer than it would take to reach the destination, it is assumed the Ship would have stopped when it reached the destination.
Belt Contract:
  • Belts exists with a specific (x,y) location; users may travel to Belts to earn Ore.
  • Each Belt has its own capacity; this is a measure of the total amount of Ore that can be mined from the Belt.
  • Once the Belt has reached its capacity it is considered "depleted" and miners will no longer earn Ore.
  • The Belts Admin may add a new Belt at any time; when a belt is added, a hash of what the crimes outcomes and Ore breakdown are going to be is recorded.
  • After a Belt is added, it must also be "published" to include the crimes outcomes and its Ore breakdown.
  • Crimes outcomes and Ore breakdown are used when determining the results of mining and roaming.
  • When published, the hash of the crimes outcomes and Ore breakdown must match the hash that was recorded when the Belt was initially added.
Outposts Contract:
  • Outposts exist at a specific (x,y) location and are valid destinations for users. Outposts include The Citadel.
  • When returning from mining, Miners may elect to go to any valid Outpost to store their Ore.
ConstructionBay Contract:
  • This contract is used to sell Ships to users through a Dutch Auction.
  • While an auction is open, users may deposit a specified token to use as a bid.
  • Well structured logic to support fee-on-transfer tokens.
  • The bidding logic is implemented using off-chain logic, so we cannot give an assessment in regards to security.
  • When the auction end time has passed, the Construction Admin may submit a winning bid amount; the logic to determine the winning bid is also implemented using off-chain logic so we cannot provide an assessment in regards to security.
  • If the Construction Admin does not select a winning bid after the auction's end and before the "winning submission delay" has passed, an auction is considered "neglected".
  • Users may refund any tokens deposited to a neglected auction.
  • Once the winning bid has been selected, users must "reveal" whether their bid was a winning bid.
  • Any user who has bid at least as much as the selected winning bid amount is considered to have submitted a winning bid.
  • All users with winning bids will purchase Ships at the price of the selected winning bid amount, even if their bid may have been higher.
  • A hash signed by both the user and the "system signer" is required to verify a bid before it is revealed; generating signatures requires the use of off-chain logic.
  • If a user has submitted a winning bid, they will win either the specified number of Ships on their bid or the remaining number of Ships in the auction.
  • All bids will pay the tax fee amount based on the number of ships the user bid on; the tax fee is allocated as rewards for users who participated in the auction.
  • Each auction has its own associated tax rate and may vary between auctions.
  • Winning bids will pay an additional DAO fee calculated from the number of ships the user actually received; the DAO fee is held in the contract and can be sent to the DAO address by anyone.
  • Users will receive a relative proportion of the total rewards based on their deposit size.
  • If a user has submitted a bid and not revealed whether it is a winning bid after the auction's end and before the "finality delay" has passed, they may be "punished" by any user.
  • The original hash used to submit the bid signed by the user being punished and the hash from the "system signer" is required to verify the user being punished submitted a valid bid; generating signatures requires the use of off-chain logic.
  • All punished bids will pay the auction's tax fee amount which is allocated as rewards for users who participated in the auction.
  • If a punished bid would have been a winning bid they pay an additional abandonment tax which is similarly allocated for users who participated in the auction.
  • A punished winning bid will also forfeit any Ships they would have won.
  • The remaining tokens after taxes are returned to the user who placed the bid when their bid is revealed or punished.
  • Users may claim their participation rewards at any time if they have revealed their bid on time.
  • The deployer is granted the Construction Admin and Default Admin roles upon deployment.
  • The Construction Admin make create a new auction at any time.
Game Contract:
  • This contract serves as the main way for users to interact with the game.
  • Users may stake a revealed Ship; this will transfer the Ship to the contract and allow users to interact with the game. You are minted a Pilot's badge as evidence of your staked Ship.
  • Users may unstake any Ship that is currently at The Citadel, burning a Pilot's badge in the process.
  • Users may travel to any valid destination given they have sufficient fuel and their Ship state is idle or their ship was returning to an Outpost and has arrived.
  • Fuel cost in Ore is calculated depending on the distance to the destination, the Ship's fuel efficiency, and the Ship's reactor level; 1/3 of all fuel used for travel is sent to the DAO address.
  • A Marauder Ship may begin "roaming" if its current state is idle and they are at the specified belt.
  • Marauders will receive an Ore amount on each block based on their Ship's power, the belt's "corruption" level, and the total Ore that was mined.
  • Users must claim their rewards separately after they stop roaming. A DAO fee is taken from the pending rewards and transferred to the DAO address.
  • A Mining Ship may begin mining if its current state is idle, they are at the specified belt, and the belt has enough capacity for the Ship.
  • Miners will earn rewards per block based on their Ship's power, a randomly generated Ore multiplier, and the amount of time spent drilling.
  • When a Miner stops mining, they may either return the Ore to an Outpost themselves or call a Transport Ship to return the Ore for them.
  • If a Miner transports the Ore themselves and there are Marauders at the Belt, they will pay a tax to Marauders based on a randomly generated number and the crimes outcomes for the Belt.
  • In the worst-case scenario, the mining Ship may be destroyed and subsequently the Ship's corresponding PilotBade is burned.
  • Alternatively, if they call a Transport Ship to collect the Ore, the Miner will pay a predetermined tax based on the Belt's corruption.
  • Users must reveal the mining outcome and then claim any pending rewards separately after they stop mining. A DAO fee is taken from the pending rewards and transferred to the DAO address.
  • Users may upgrade the stats of Ships, one level at a time, that are at The Citadel by spending Ore.
  • Upgrades will cost more Ore and take more time to complete depending on which trait and to which level they are being upgraded.
  • Users must separately complete the upgrade after the required upgrade time has passed.
  • The deployer gets the Adder, Publisher, and Default Admin role upon deployment.
  • The Adder role may add a new belt or Outpost at any time.
RandomOracle and RandomOracleConsumer Contracts:
  • These contracts are used to provide the random numbers used to generate Ship's traits and determine mining outcomes.
  • A keyhash for VRF generation and the LINK price used to fund the generation is declared upon deployment.
  • Any address with the Requester role may request a random number. After the random number is generated, the RandomOracle will call a provided function while supplying the newly generated number.
CitadelGovernor Contract:
  • This contract is used to manage the DAO voting and proposal functionality within the ecosystem.
  • Users will receive one vote per PilotsBadge token.
  • Users may vote for, vote against, or abstain from a proposal.
  • Users also have the option to vote through the use of a signed message, allowing for a gasless vote for the user.
  • Votes for and abstained from a proposal will count towards a quorum.
  • A "quorum threshold" of 4% of the total token supply must be reached to pass a proposal.
  • Users with more tokens than the "proposal threshold" are proposers, allowing them to submit a proposal.
  • After a proposal is submitted, votes may not be cast until the "voting delay" has passed; this allows users to delegate and stake/unstake tokens as necessary.
  • The proposal will remain open for votes for the entire "voting period".
  • After the voting period, if a quorum is reached and the proposal is passed, the execution of the proposal is delegated to a Timelock contract.
  • The Timelock contract will not execute the function until a further delay has passed.
  • Users may change the previously described quorum threshold, proposal threshold, voting delay, and voting period through a successful proposal.
  • A veto address is declared upon deployment and may cancel any pending transaction.
  • The veto address may relinquish its role at any time.
CitadelTimelock Contract:
  • This contract is used in conjunction with the CitadelGovernor to execute successful proposals.
  • The deployer and this contract are granted the Timelock Admin role upon deployment. We recommend that only the CitadelTimelock contract retain this role.
  • A provided list of addresses are granted the Proposer role upon deployment. We recommend that only the CitadelGovernor contract be granted this role.
  • Similarly, another list of addresses is granted the Executor role upon deployment.
  • The Timelock Admin role may grant the Timelock Admin, Proposer, or Executor role to any address at any time.
  • The Proposer role may schedule an operation that will be executed by the timelock after the delay period has passed.
  • The Proposer role may also cancel a pending operation before it is executed.
  • The Executor role may execute an operation once the delay period has passed.
  • The delay period may be updated through successful execution of a proposal.
CitadelProxyAdmin Contract:
  • This contract serves as the Admin contract for the various proxy contracts within the project.
  • The owner may upgrade any proxy implementation at any time.
  • Addresses may be authorized to upgrade specific proxies.
  • The owner may set an address as authorized for a specific proxy at any time.
StandardRootTunnel & StandardChildTunnel Contracts:
  • These contracts are used to pass messages between the Polygon Chain and the Ethereum Chain.
  • Addresses with the Sender role may pass a message to a Tunnel contract.
  • The Tunnel contract will then send a message to the Child contract.
  • The Child contract will interpret the message and execute the appropriate functions and logic.
GovernanceRootTunnel & GovernanceChildTunnel Contracts:
  • These contracts function in the same way as the StandardRootTunnel and StandardChildTunnel contracts but are used to relay information from the CitadelTimelock related to governance and proposals.
Dock Contract:
  • This contract is used to manage the transfer of Ships and Ore between the Polygon Chain and the Ethereum Chain.
  • Users may "dock" up to 20 of their Ships at a time.
  • Docking will store the Ships in the Dock contract.
  • The Dock contract will then pass an "undock" message to the appropriate Tunnel.
  • The user is then transferred or, if they do not already exist, minted the Ships on the receiving chain.
  • Users may similarly "bank" any amount of Ore.
  • The Dock will burn the Ore on the sending chain and pass an "unbank" message to the appropriate Tunnel.
  • The user is then minted the same amount of Ore on the receiving network.

Audit Results

Vulnerability CategoryNotesResult
Arbitrary Jump/Storage WriteN/APASS
Centralization of Control
  • The team retains ownership functionality described above, with the intent of transferring ownership to the CitadelGovernor contract.
  • Multiple contracts rely on off-chain logic.
  • Proxy contracts may potentially be updated by the team rather than the CitadelGovernor contract.
WARNING
Compiler IssuesN/APASS
Delegate Call to Untrusted ContractN/APASS
Dependence on Predictable VariablesN/APASS
Ether/Token TheftN/APASS
Flash LoansN/APASS
Front RunningN/APASS
Improper EventsN/APASS
Improper Authorization SchemeN/APASS
Integer Over/UnderflowN/APASS
Logical IssuesN/APASS
Oracle IssuesN/APASS
Outdated Compiler VersionN/APASS
Race ConditionsN/APASS
ReentrancyN/APASS
Signature IssuesN/APASS
Unbounded LoopsN/APASS
Unused CodeN/APASS
Overall Contract Safety PASS

Contract Source Summary and Visualizations

Name

Address/Source Code

Visualized
(Hover-Zoom Recommended)

BeltsLib

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

Game

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

OutpostsLib

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

RewardsLib

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

Staking

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

TokenTraits

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

TravelLib

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

UpgradesLib

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

CitadelGovernor

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

CitadelProxyAdmin

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

CitadelTimelock

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

RandomOracle

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

RandomOracleConsumer

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

BaseShips

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

BaseShipTraits

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

DutchShips

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

FixedPriceShips

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

FixedShipTraits

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

GenesisShipTraits

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

Ore

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

PilotBadge

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

StandardChildTunnel

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

StandardRootTunnel

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

GovernanceChildTunnel

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

GovernanceRootTunnel

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

Dock

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.

ConstructionBay

GitHub (not yet deployed)

Inheritance Chart.  Function Graph.