Collar Quest Battle And Breeding
Smart Contract Audit Report
Audit Summary
Collar Quest is building a new platform in which users can 'Breed' their NFTs. In addition, authorized users can execute 'Battles'.
For this audit, we reviewed Collar Quest's Breeding contract at 0xc9b47dcb7f55175e03483dc626158a37e7a73516 on the Ethereum Mainnet, and Battle contract at 0x6f289537e5ac198f43a9399861f0864f6708ede8 on the Goerli Testnet.
We previously reviewed the project team's SPARCE contracts here.
Audit Findings
All findings have been resolved, though some centralized aspects are present.
Date: September 27th, 2022.
Updated: September 28th, 2022 to reflect response from the project team.
Updated: November 22nd, 2022 to reflect changes to the Battle contract from 0x9c04d75924fd2e06c5d8fa986e4de1bd11342735 to 0x6f289537e5ac198f43a9399861f0864f6708ede8 on the Goerli Testnet.Finding #1 - Breeding & Battle - Low (Resolved)
Description: A deadline is included in the message hash of passed signatures, but there is no check to see if the deadline has passed.
Risk/Impact: Signatures can still be used after their specified deadlines.
Recommendation: The project team should require that the current timestamp does not exceed the deadline if they wish to enforce it.
Update: Signatures must now be used before their deadline has passed.
Contracts Overview
Breeding Contract:
- As the contracts are implemented with Solidity v0.8.x, they are safe from any possible overflows/underflows.
Battle Contract:
- This contract allows users to breed matrons and sires of different SPARC-E ID's.
- When breeding, the token IDs, gene count, deadline, and signature are all specified.
- These values are verified to have originated from a signed message from a Breed Manager address.
- A user can use a single signature to breed multiple times.
- The user must own both tokens in order to breed.
- Each parent's breed count is incremented after a breed. A sire or matron cannot breed if it results in either of their breed counts exceeding the maximum breed count.
- A TAG fee and a GeM fee are both taken from the user upon breeding.
- The TAG fee is a flat specified amount, and the GeM fee is calculated using the sum of the specified fee corresponding to the birthcount of each parent.
- The TAG fee is transferred to the Treasury address; the GeM fee is burned.
- A new Breed is then created with attributes consisting of its parents, gene count, breed time, and hatched status.
- Any user can hatch a Breed once the hatch time has passed from the time of its creation.
- Neither parent can breed again until their current Breed has hatched.
- When a Breed is hatched, a new SPARC-E is minted to the original breeder with its assigned gene count.
- The deployer is granted the Admin Role upon creation.
- The Admin Role can grant or revoke the Breed Manager Role from any address at any time.
- The Admin Role can update the SPARC-E, Treasury, TAG, and GeM addresses at any time.
- The Admin Role can update the GeM fee for any birthcount at any time.
- The Admin Role can update the maximum breed count, hatch time, and TAG fee at any time.
- A battle can be executed by an Admin or a Battle Manager at any time.
- Two signatures along with a reward amount and deadline must be passed. These signatures are verified to have originated from addresses with the Battle Manager One and Battle Manager Two roles, respectively.
- A signature can only be used by a manager address once.
- A signature cannot be used if its deadline has passed.
- The caller of the battle will then have their claimable reward amount incremented by the specified reward amount.
- If the user has not claimed before, their last claim time is set to the current timestamp.
- A user can claim rewards once the claim wait duration has passed since their last claim time.
- A user cannot claim more than the maximum claim limit at once.
- A user cannot claim if they have been added to the Blacklist.
- The claimed reward amount is transferred to the user in the form of GeM tokens.
- The team must ensure that the contract holds a sufficient GeM balance in order to allow claims.
- The Admin should not grant the Battle Manager One Role to any user other than the contract's first manager, and should not grant the Battle Manager Two Role to any user other than the contract's second manager. If this does occur, it may become possible for a signature to be reused.
- The deployer is granted the Admin Role upon creation.
- The Admin Role can grant or revoke the Battle Manager One or Battle Manager Two Role from any address at any time.
- The Admin can update the GeM address at any time.
- The Admin can update the maximum claim limit and claim wait duration at any time.
Audit Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Jump/Storage Write | N/A | PASS |
Centralization of Control |
|
WARNING |
Compiler Issues | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Ether/Token Theft | N/A | PASS |
Flash Loans | N/A | PASS |
Front Running | N/A | PASS |
Improper Events | N/A | PASS |
Improper Authorization Scheme | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Logical Issues | N/A | PASS |
Oracle Issues | N/A | PASS |
Outdated Compiler Version | N/A | PASS |
Race Conditions | N/A | PASS |
Reentrancy | N/A | PASS |
Signature Issues | N/A | PASS |
Unbounded Loops | N/A | PASS |
Unused Code | N/A | PASS |
Overall Contract Safety | PASS |
Breeding Contract
($) = payable function
# = non-constant function
+ [Int] IAccessControl
- [Ext] hasRole
- [Ext] getRoleAdmin
- [Ext] grantRole #
- [Ext] revokeRole #
- [Ext] renounceRole #
+ [Int] IERC165
- [Ext] supportsInterface
+ [Int] IERC721 (IERC165)
- [Ext] balanceOf
- [Ext] ownerOf
- [Ext] safeTransferFrom #
- [Ext] transferFrom #
- [Ext] approve #
- [Ext] getApproved
- [Ext] setApprovalForAll #
- [Ext] isApprovedForAll
- [Ext] safeTransferFrom #
+ [Int] ICollarQuest (IERC721)
- [Ext] spawnSparce #
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] decimals
- [Ext] symbol
- [Ext] name
- [Ext] getOwner
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
- [Ext] burn #
- [Ext] burnFrom #
+ Context
- [Int] _msgSender
- [Int] _msgData
+ Validator
- [Pub] #
- [Pub] validateSig
+ [Lib] ECDSA
- [Int] recover
- [Int] toSignedMessageHash
+ [Lib] Strings
- [Int] toString
- [Int] toHexString
- [Int] toHexString
+ AccessControl (Context, IAccessControl)
- [Pub] hasRole
- [Int] _checkRole
- [Pub] getRoleAdmin
- [Pub] grantRole #
- modifiers: onlyRole
- [Pub] revokeRole #
- modifiers: onlyRole
- [Pub] renounceRole #
- [Int] _setupRole #
- [Int] _setRoleAdmin #
- [Prv] _grantRole #
- [Prv] _revokeRole #
+ BreedingCore (AccessControl, Validator)
- [Int] payBreedFee #
+ Breeding (BreedingCore)
- [Pub] #
- [Ext] setCollarQuest #
- modifiers: onlyRole
- [Ext] setTotalBreedCount #
- modifiers: onlyRole
- [Ext] setHatchTime #
- modifiers: onlyRole
- [Ext] setTreasury #
- modifiers: onlyRole
- [Ext] setTag #
- modifiers: onlyRole
- [Ext] setGeM #
- modifiers: onlyRole
- [Ext] setTagFee #
- modifiers: onlyRole
- [Ext] setGeMFee #
- modifiers: onlyRole
- [Ext] breed #
- modifiers: notBreedWhen
- [Ext] eggsHatch #
Battle Contract
($) = payable function
# = non-constant function
+ [Int] IAccessControl
- [Ext] hasRole
- [Ext] getRoleAdmin
- [Ext] grantRole #
- [Ext] revokeRole #
- [Ext] renounceRole #
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] transfer #
- [Ext] allowance
- [Ext] approve #
- [Ext] transferFrom #
- [Ext] mint #
+ [Int] IERC721
- [Ext] balanceOf
- [Ext] ownerOf
- [Ext] safeTransferFrom #
- [Ext] transferFrom #
- [Ext] approve #
- [Ext] getApproved
- [Ext] setApprovalForAll #
- [Ext] isApprovedForAll
- [Ext] safeTransferFrom #
+ Context
- [Int] _msgSender
- [Int] _msgData
+ AccessControl (Context, IAccessControl)
- [Pub] hasRole
- [Int] _checkRole
- [Pub] getRoleAdmin
- [Pub] grantRole #
- modifiers: onlyRole
- [Pub] revokeRole #
- modifiers: onlyRole
- [Pub] renounceRole #
- [Int] _setupRole #
- [Int] _setRoleAdmin #
- [Prv] _grantRole #
- [Prv] _revokeRole #
+ Validator
- [Pub] #
- [Pub] validateSig
+ [Lib] ECDSA
- [Int] recover
- [Int] toSignedMessageHash
+ [Lib] Strings
- [Int] toString
- [Int] toHexString
- [Int] toHexString
+ Battle (AccessControl, Validator)
- [Pub] #
- [Ext] setSPARCE #
- modifiers: onlyRole
- [Ext] setGeM #
- modifiers: onlyRole
- [Ext] setMaxClaimLimit #
- modifiers: onlyRole
- [Ext] setClaimTimeStamp #
- modifiers: onlyRole
- [Ext] battleReward #
- [Ext] claim #
- [Ext] getNonce
- [Ext] isSigned
About SourceHat
SourceHat (formerly Solidity Finance - founded in 2020) has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1700+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!
Contact us today to get a free quote for a smart contract audit of your project!
What is a SourceHat Audit?
Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.
How Do I Interpret the Findings?
Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:
- High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
- Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
- Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
- Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.