TRACKToken
Smart Contract Audit Report
Audit Summary
TRACKToken is a new ERC-20 token with static reward distribution based on the floor price of a basket of popular NFT collections.
For this audit, we reviewed the following contracts on the Kovan Testnet:
- TRACKToken contract at 0x287E10860Eb6c831D62A4624A17aE58823f452a2.
- UFragmentsPolicy contract at 0x5693E37C92D713f977D498e246d513Bc79Ea767d.
- Orchestrator contract at 0x2eD32611A5253a312fb8eAEF0bdbB575EF796bf4.
- TargetPriceOracle contract at 0xAB626122c4F063859908e7Db0E3716Fe44082989.
- MarketPriceOracle contract at 0xa589Da0362CbCB49ACc5E74f5504b1F7487b6313.
Audit Findings
Please ensure trust in the team prior to investing as they have notable control in the ecosystem.
Date: May 11th, 2022.
Updated: May 19th, 2022 to reflect resolved issues by the team.Finding #1 - TRACKToken - High (Resolved)
Description: The LP Pair sync function is not called when a recalibration occurs.
Risk/Impact: Malicious users will be able to call theskim()
function to steal excess tokens from the liquidity pool after recalibrations occur.
Recommendation: The team should include a call to thesync()
function within therecalibrate()
function to ensure the LP pool is synced during each recalibration.
Resolution: The team has included a call tosync()
during recalibration.Finding #2 - Orchestrator - Medium (Resolved)
Description: TheremoveTransaction()
function intends to remove an element from the transaction list;transactions.pop
is incorrectly used so no element is ever removed.
Risk/Impact: There is no functionality for the team to remove a transaction from the list.
Recommendation: We recommend fixing the syntax error by usingtransactions.pop()
in theremoveTransaction()
function.
Resolution:The team has fixed this syntax error.Finding #3 - Orchestrator - Medium (Resolved)
Description:Therecalibrate()
function intends to process a list of transactions and continue in the event that a single transaction execution fails.
Risk/Impact: The entire function call will be reverted in the event that a single transaction execution fails.
Recommendation: Therecalibrate()
function should handle failed transactions in a manner that does not affect other transactions executed in the loop.
Resolution: The team has allowed for a single failed transaction in their list to not revert the entire function call.Finding #4 - TargetPriceOracle - Low (Resolved)
Description: The unofficial MAYC collection is linked in the list of APIs used for floor price retrieval.
Risk/Impact: Using this unofficial MAYC collection will impact cumulative floor price calculations as the unofficial collection linked has a floor price of 0.01 ETH as compared to the 19.79 ETH floor price of the official collection noted at the time of writing this report.
Recommendation: The team should use the official MAYC's OpenSea API, to ensure accurate floor price retrieval for recalibration operations.
Resolution:The team has included the official MAYC API link to be referenced during cumulative floor price calculations.Finding #5 - Initializable, Ownable - Informational (Resolved)
Description: These contracts include a______gap
storage variable which is intended for upgradeability, but there is no upgradeability functionality present in the platform..
Recommendation: The______gap
variables should be removed to reduce contract size and deployment costs.
Resolution: The team has removed the______gap
variables.Finding #6 - TRACKToken, UFragmentsPolicy, MarketPriceOracle, Orchestrator - Informational
Description: Although the SafeMath library is utilized, the contract is implemented with Solidity v0.8.x which has built-in overflow checks.
Recommendation: SafeMath could be safely removed to reduce contract size and deployment costs.
Contracts Overview
TargetPriceOracle Contract:
- As the contracts are implemented with Solidity v0.8.0 or use SafeMath, they are safe from any possible overflows/underflows.
MarketPriceOracle Contract:
- This contract utilizes a Chainlink Oracle to fetch floor price market data for the BAYC, MAYC, Invisible Friends, CloneX, Azuki, Doodles, Cool Cats, VeeFriends, World of Women, and CyberKongz NFT Collections.
- Both the Oracle contract and Off-chain Node used during Chainlink requests in this contract are beyond the scope of this audit.
- When a floor price is requested, HTTP Get requests are made to the OpenSea API for each collection via Chainlink, and then summed to a single floor price representing all collections considered.
- In addition to this cumulative floor price, the team specifies a base floor price. The ratio of the cumulative floor price to the base floor price is used as the target rate by the UFragmentsPolicy contract during recalibration.
- The owner can set the base floor price at any time.
- The owner can set the ChainLinkOracle at any time.
- The owner can replace an API link used for floor price data at any time; there will only ever be 10 NFT collections considered.
UFragmentsPolicy Contract:
- This contract stores a team specified "market price" to be used in "recalibration" operations; supply adjustments to TRACKToken based on market & target prices of targeted NFTs.
- When recalibrating, if the target rate calculated by the TargetPriceOracle does not exceed the "market price" value stored here, $TRACK holders will be paid out. Otherwise, their holdings are decreased.
- Only the owner can specify the price stored here.
Orchestrator Contract:
- This contract implements recalibration rewards for $TRACK holders based on the floor price of a basket of popular NFT collections.
- Upon initialization, the owner's address and TRACKToken contract are specified, as well as default values to be used during recalibration.
- Every 24 hours, on the 20th hour there is a 15 minute window during which recalibration can occur.
- Only the Orchestrator contract can invoke recalibration, given we are in the specified recalibration window.
- During recalibration, a target rate is received from the TargetPriceOracle; this is the ratio of the cumulative floor price of the NFT collections to the base floor price specified by the team.
- During recalibration, an "exchange rate" determined by the team is received from the MarketPriceOracle.
- If the difference between the exchange rate and target rate is more than 2.5% of the target rate, these values are within the deviation threshold and there is no change made to the total supply of $TRACK.
- If they are not within this threshold, the supply of TRACK will be changed based on how much the target rate varies from the exchange rate, proportional to the total supply / target rate. A "recalibration lag" is applied by dividing this value by the recalibration lag to ensure there is no dramatic change to the total supply of TRACK.
- If the difference between this target rate and exchange rate is positive, then the total supply of TRACK will increase and be frictionlessly distributed to all holders. Otherwise, the total supply is decreased and the amount of TRACK held by each user is decreased.
- The owner can set the address of the TargetPriceOracle, MarketPriceOracle, and Orchestrator contract at any time.
- The owner can set the deviation threshold used to determine changes in TRACK supply.
- The owner can set the recalibration lag applied during supply change calculations.
- The owner can set the minimum recalibration time interval, recalibration window offset, or recalibration window length.
TRACKToken Contract:
- The contract is an entry point to recalibration operations for $TRACK holders.
- Upon deployment, the UFragmentsPolicy contract address is specified.
- Only Externally Owned Accounts can invoke recalibration, never a contract address.
- Following the recalibration done by the UFragmentsPolicy contract, additional transactions are carried out as specified by the team.
- The owner can add and remove transactions to be executed following a $TRACK recalibration. The team should exercise caution when adding transactions as the loop executing them could be costly and reach the block gas limit.
- The owner can set whether an included transaction is enabled or not.
- The TRACKToken contract implements static rewards for holders based on the floor price of a basket of popular NFT Collections.
- Upon initialization, the owner receives the initial total supply of 596,000 $TRACK tokens.
- The UFragmentsPolicy contract is intended to be the sole invoker of this contract's recalibration function.
- During recalibration, the $TRACK total supply is increased or decreased depending on a supply change value specified by the UFragmentsPolicy contract.
- During recalibration, if the $TRACK total supply is increased, the ratio of reflection tokens to $TRACK tokens is decreased. This increases the amount of $TRACK held by each account in a frictionless fashion.
- During recalibration, when the $TRACK total supply is decreased, the ratio of reflection tokens to $TRACK tokens is increased, which decreases the amount of $TRACK held by each account in a frictionless fashion.
- Transfers of $TRACK always charge a 5% fee taken from the sender's account.
- During the transfer, 1% is given to a wallet controlled by the team and 4% is given to another wallet controlled by the team.
- The owner can set the address of both fee wallets.
- The owner can set the address of the Uniswap pair to be used with this token at any time.
- The owner can set the address of the UFragmentsPolicy contract to be used at any time.
- This contract complies with the ERC-20 standard.
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 | No events are emitted on transfers to fee wallets. This can affect Etherscan's front-end. | WARNING |
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 |
TargetPriceOracle Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] BufferChainlink
- [Int] init
- [Int] fromBytes
- [Prv] resize
- [Prv] max
- [Int] truncate
- [Int] write
- [Int] append
- [Int] append
- [Int] writeUint8
- [Int] appendUint8
- [Prv] write
- [Int] writeBytes20
- [Int] appendBytes20
- [Int] appendBytes32
- [Prv] writeInt
- [Int] appendInt
+ [Lib] CBORChainlink
- [Prv] encodeFixedNumeric
- [Prv] encodeIndefiniteLengthType
- [Int] encodeUInt
- [Int] encodeInt
- [Int] encodeBytes
- [Int] encodeBigNum
- [Int] encodeSignedBigNum
- [Int] encodeString
- [Int] startArray
- [Int] startMap
- [Int] endSequence
+ [Lib] Chainlink
- [Int] initialize
- [Int] setBuffer
- [Int] add
- [Int] addBytes
- [Int] addInt
- [Int] addUint
- [Int] addStringArray
+ [Int] ENSInterface
- [Ext] setSubnodeOwner #
- [Ext] setResolver #
- [Ext] setOwner #
- [Ext] setTTL #
- [Ext] owner
- [Ext] resolver
- [Ext] ttl
+ [Int] LinkTokenInterface
- [Ext] allowance
- [Ext] approve #
- [Ext] balanceOf
- [Ext] decimals
- [Ext] decreaseApproval #
- [Ext] increaseApproval #
- [Ext] name
- [Ext] symbol
- [Ext] totalSupply
- [Ext] transfer #
- [Ext] transferAndCall #
- [Ext] transferFrom #
+ [Int] ChainlinkRequestInterface
- [Ext] oracleRequest #
- [Ext] cancelOracleRequest #
+ [Int] PointerInterface
- [Ext] getAddress
+ ENSResolver_Chainlink
- [Pub] addr
+ ChainlinkClient
- [Int] buildChainlinkRequest
- [Int] sendChainlinkRequest #
- [Int] sendChainlinkRequestTo #
- [Int] cancelChainlinkRequest #
- [Int] setChainlinkOracle #
- [Int] setChainlinkToken #
- [Int] setPublicChainlinkToken #
- [Int] chainlinkTokenAddress
- [Int] chainlinkOracleAddress
- [Int] addChainlinkExternalRequest #
- modifiers: notPendingRequest
- [Int] useChainlinkWithENS #
- [Int] updateChainlinkOracleWithENS #
- [Prv] encodeRequest
- [Int] validateChainlinkCallback #
- modifiers: recordChainlinkFulfillment
+ [Lib] SafeMath
- [Int] add
- [Int] sub
- [Int] sub
- [Int] mul
- [Int] div
- [Int] div
- [Int] mod
- [Int] mod
+ Ownable
- [Pub] #
- [Pub] owner
- [Pub] isOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ TargetPriceOracle (Ownable, ChainlinkClient)
- [Pub] #
- [Pub] setbaseFloorprice #
- modifiers: onlyOwner
- [Pub] setChainlinkOracle #
- modifiers: onlyOwner
- [Pub] setNewAPI #
- modifiers: onlyOwner
- [Ext] requestFloorPrice #
- [Pub] fulfill #
- modifiers: recordChainlinkFulfillment
- [Ext] getData
MarketPriceOracle Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Ownable
- [Pub] #
- [Pub] owner
- [Pub] isOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ MarketPriceOracle (Ownable)
- [Ext] pushReport #
- modifiers: onlyOwner
- [Ext] getData
UFragmentsPolicy Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] SafeMathInt
- [Int] mul
- [Int] div
- [Int] sub
- [Int] add
- [Int] abs
+ [Lib] UInt256Lib
- [Int] toInt256Safe
+ Initializable
- [Prv] isConstructor
+ Ownable (Initializable)
- [Pub] initialize #
- modifiers: initializer
- [Pub] owner
- [Pub] isOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ [Lib] SafeMath
- [Int] mul
- [Int] div
- [Int] sub
- [Int] add
- [Int] mod
+ [Int] TRACKToken
- [Ext] recalibrate #
- [Ext] totalSupply
+ [Int] IOracle
- [Ext] getData #
+ UFragmentsPolicy (Ownable)
- [Ext] recalibrate #
- modifiers: onlyOrchestrator
- [Ext] setTargetPriceOracle #
- modifiers: onlyOwner
- [Ext] setMarketOracle #
- modifiers: onlyOwner
- [Ext] setOrchestrator #
- modifiers: onlyOwner
- [Ext] setDeviationThreshold #
- modifiers: onlyOwner
- [Ext] setrecalibrateLag #
- modifiers: onlyOwner
- [Ext] setrecalibrateTimingParameters #
- modifiers: onlyOwner
- [Pub] initialize #
- modifiers: initializer
- [Pub] inrecalibrateWindow
- [Prv] computeSupplyDelta
- [Prv] withinDeviationThreshold
Orchestrator Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ Initializable
- [Prv] isConstructor
+ Ownable (Initializable)
- [Pub] initialize #
- modifiers: initializer
- [Pub] owner
- [Pub] isOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ [Int] UFragmentsPolicy
- [Ext] recalibrate #
+ Orchestrator (Ownable)
- [Pub] #
- [Ext] recalibrate #
- [Ext] addTransaction #
- modifiers: onlyOwner
- [Ext] removeTransaction #
- modifiers: onlyOwner
- [Ext] setTransactionEnabled #
- modifiers: onlyOwner
- [Ext] transactionsSize
- [Int] externalCall #
TRACKToken Contract
($) = payable function
# = non-constant function
Int = Internal
Ext = External
Pub = Public
+ [Lib] SafeMathInt
- [Int] mul
- [Int] div
- [Int] sub
- [Int] add
- [Int] abs
+ Initializable
- [Prv] isConstructor
+ Ownable (Initializable)
- [Pub] initialize #
- modifiers: initializer
- [Pub] owner
- [Pub] isOwner
- [Pub] renounceOwnership #
- modifiers: onlyOwner
- [Pub] transferOwnership #
- modifiers: onlyOwner
- [Int] _transferOwnership #
+ [Lib] SafeMath
- [Int] mul
- [Int] div
- [Int] sub
- [Int] add
- [Int] mod
+ [Int] IERC20
- [Ext] totalSupply
- [Ext] balanceOf
- [Ext] allowance
- [Ext] transfer #
- [Ext] approve #
- [Ext] transferFrom #
+ ERC20Detailed (Initializable, IERC20)
- [Pub] initialize #
- modifiers: initializer
+ [Int] ISync
- [Ext] sync #
+ TRACKToken (ERC20Detailed, Ownable)
- [Ext] set1percentDAOwallet #
- modifiers: onlyOwner
- [Ext] set4percentDAOwallet #
- modifiers: onlyOwner
- [Ext] setMonetaryPolicy #
- modifiers: onlyOwner
- [Ext] setPbaseEthPairAddress #
- modifiers: onlyOwner
- [Ext] recalibrate #
- modifiers: onlyMonetaryPolicy
- [Pub] initialization #
- modifiers: initializer
- [Pub] totalSupply
- [Pub] balanceOf
- [Pub] transfer #
- modifiers: validRecipient
- [Pub] allowance
- [Pub] transferFrom #
- modifiers: validRecipient
- [Pub] approve #
- [Pub] increaseAllowance #
- [Pub] decreaseAllowance #
About SourceHat
SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ 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.