Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · March 2025

Prediction Market

for Trueo

Trueo engaged Guardian to review the security of their decentralized prediction market platform, enabling users to forecast outcomes of real world events. From the 13th of January to the 27th of January, a team of 6 auditors reviewed the source code in scope.

Published
Review window
January 13 to 27, 2025
Language
Solidity
Chains
Base
Sector
Derivatives
  • 1 Critical
  • 6 High
  • 13 Medium
  • 19 Low
  • 0 Informational

10 resolved · 2 partially resolved · 27 acknowledged

Scope

Overview

Trueo engaged Guardian to review the security of their decentralized prediction market platform, enabling users to forecast outcomes of real world events. From the 13th of January to the 27th of January, a team of 6 auditors reviewed the source code in scope.

Issues Detected Throughout the engagement 7 High/Critical issues were uncovered and promptly remediated by the Trueo team.

Security Recommendation Given the number of High and Critical issues detected as well as additional code changes made after the main review, Guardian recommends that an independent security review of the protocol at a finalized frozen commit is conducted before deployment.

Findings 39

  1. C-01 Critical New Resolver Loses Bond After Council Reset Logical Error Resolved
    Location
    OracleBonds.sol: 120

    Description

    Proof of concept: PoC

    In the sendResolverBondToMarket function, the resolver bond is currently stored using: marketBond[_market].resolverBond = _amount;

    This approach overwrites any existing resolver bond, which is generally acceptable as only one resolution is expected.

    However, an issue arises when a market status is resetByCouncil. Here’s the problematic sequence: 1. Reset State: The council votes to reset the market, leaving the previous resolver's bond intact but not immediately returned. 2. New Proposal: A new resolver proposes a resolution, adding their bond via sendResolverBondToMarket. 3. Bond Overwrite: The resolverBond value is overwritten with the new bond, effectively discarding the previous bond value. 4. Loss of Funds: When the previous bond is sent to the disputor (to punish original resolver), the resolverBond is reset to zero, causing the new resolver to lose their bond.

    Recommendation

    To handle the situation where multiple bonds coexist during a reset, the resolver bonds should be stored incrementally and decremented appropriately when withdrawn or refunded.

    Modify sendResolverBondToMarket to increment the value by the new bond amount: marketBond[_market].resolverBond = _amount.

    Similarly, in sendBondFromMarketToSafeBox and issueBondsBackToResolver, decrement the resolver bond by the transferred amount instead of setting it to zero.

    Resolution

    Trueo Team: Resolved.

  2. H-01 High Second Challenge Period Can Be Bypassed Logical Error Acknowledged
    Location
    TruthMarket.sol: 199-209

    Description

    Proof of concept: PoC

    When the market is in the MarketStatus.ResetByCouncil state, it should wait for the secondChallengePeriod before opening for resolution to allow openEscalatedDispute. However, proposeResolution can be called immediately without waiting for the secondChallengePeriod.

    The bypass of secondChallengePeriod allows a dishonest disputor to move the market status to ResolutionProposed therefore earning the resolver's bond before the resolver has a chance to escalate the issue.

    Recommendation

    Consider moving _updateStatus execution before setting councilDecisionAt = 0 .

    Resolution

    Trueo Team: Acknowledged.

  3. H-02 High Resolver Might Not Receive The Deserved Reward Logical Error Resolved
    Location
    TruthMarket.sol: 523-525, TruthMarket.sol: 549-551

    Description

    When a market is finalized and ends with _CANCELED as the winningPosition, users can trigger withdrawFromCanceledMarket to withdraw their payment tokens.

    Within withdrawFromCanceledMarket, if bondSettled is not yet settled, it will trigger _settleBonds to handle reward payments and bond refunds or slashes.

    However, if the market previously entered a dispute or escalation state, it will transfer the reward to the safeBoxAddress when the winningPosition is _CANCELED, regardless of whether the originalOutcomeFromResolver is equal to _CANCELED.

    This will result in the resolver not receiving the deserved reward.

    Recommendation

    Only transfer reward to safeBoxAddress when winningPosition is _CANCELED and winningPosition is not equal to originalOutcomeFromResolver

    Resolution

    Trueo Team: Resolved.

  4. H-03 High Old Disputes Can Be Reused After ResetByCouncil Logical Error Resolved
    Location
    TruthMarket.sol: 199-209

    Description

    Proof of concept: PoC

    When a market is partially disputed, multiple disputes can be opened in parallel. If one of those disputes concludes first (e.g., triggering a ResetByCouncil), the market transitions back to OpenForResolution, and any unclosed disputes remain in storage, still carrying prior votes.

    Because those old disputes are never marked as permanently invalid, they can be reused when the market enters a new resolution flow—allowing a malicious council member to “revive” the partially-voted dispute and finalize it with minimal new votes.

    In the provided example:

    1. A market transitions to ResolutionProposed with a “YES” outcome.
    2. Two different disputers each open a separate dispute (Dispute #1 and Dispute #2).
    3. Dispute #1 closes first (for example, by a majority that sets ResetByCouncil), while Dispute #2 remains open but unresolvable at that moment due to the market status update.
    4. The market then resets, returning to OpenForResolution (or even ResolutionProposed again).
    5. A malicious council member can propose a new outcome, open a fresh dispute, but then re-invoke the old, unresolved Dispute #2. Because that older dispute already has partial votes recorded, the malicious user can cast a single new vote (or minimal votes) to reach a majority, effectively closing Dispute #2 and imposing its outcome—even though other council members haven’t re-voted under the new context.

    Recommendation

    Invalidate or close all existing disputes when the market transitions from SetByCouncil or ResetByCouncil back to OpenForResolution.

    Resolution

    Trueo Team: Resolved.

    Guardian Team: The new check if (_disputeIndex <= marketLastClosedDispute[_market]) revert DisputeInvalid(); is ineffective because an old dispute may have a higher index than the last closed dispute. 15

  5. H-04 High Unrestricted burn Could Lead To Unsettled Bonds Logical Error Resolved
    Location
    TruthMarket.sol: 324-330

    Description

    burn can be called by anyone at any market state, allowing users to burn their yes and no tokens to retrieve the deposit tokens.

    This could cause issues in a scenario where the market is finalized, especially when the winning position is _CANCELED. Instead of calling withdrawFromCanceledMarket, users might choose to call burn to reclaim their deposit tokens.

    This would prevent _settleBonds from being triggered, causing all bonds and rewards to remain stuck and unresolved.

    Recommendation

    Consider restricting burn so it can only be called when the market is not finalized. Additionally, make settle bonds publicly callable in cases where no token holders decide to redeem or withdraw their tokens.

    Resolution

    Trueo Team: Resolved.

  6. H-05 High Locked Dispute Bonds After Escalation Reset DOS Resolved
    Location
    OracleCouncilV2.sol: 421

    Description

    Proof of concept: PoC

    When a dispute is resolved by the council and its escalation leads to a Reset result, any user with an “unclosed dispute” bond remains unable to claim it because reopenMarketForDisputes sets marketClosedForDisputes to false.

    Consequently, once the market transitions to a new proposal, if it finalizes with no disputes, the dispute is never recognized as “closed” or “canceled,” preventing claimUnclosedDisputeBonds from succeeding. The disputer’s bond remains locked, and they are unable to recover their funds.

    Recommendation

    Update canDisputorClaimbackBondFromUnclosedDispute to allow bond retrieval if the market has been reset, rather than strictly requiring it to be “closed for disputes” or “finalized.” This ensures no user is stuck with an unclaimable dispute bond after the escalation reset.

    Resolution

    Trueo Team: Resolved.

  7. H-06 High Token Holder Vote Cannot Be Disputed Logical Error Acknowledged
    Location
    Global

    Description

    The current implementation dictates that once an escalation is decided by a token holders' vote, the market either resolves to Finalized or reverts to OpenForResolution, with no mechanism to dispute this decision.

    However, according to the documentation, a third challenge window should exist, enabling any holder of 250,000 TRUE tokens to dispute the escalation result and escalate the matter to the Attesters.

    The absence of this challenge window introduces a vulnerability where large TRUE token holders could manipulate the vote for financial gain, leaving other stakeholders without any recourse to contest the outcome.

    Recommendation

    Introduce a third challenge window as outlined in the documentation, ensuring that escalation decisions can be disputed and reviewed by the Attesters.

    Resolution

    Trueo Team: Acknowledged.

  8. M-01 Medium NoToken Cap May Be Exceeded During Minting Logical Error Resolved
    Location
    TruthMarket.sol: 312

    Description

    The mint function enforces the yesNoTokenCap, but it only checks the supply of Yes tokens. This creates a potential issue where the supply of No tokens can exceed the cap if there is an imbalance between the total supply of Yes and No tokens.

    This imbalance can occur because Yes tokens can be burned (since YesNoToken is an ERC20Burnable contract).

    For example:

    • yesNoTokenCap = 100
    • Initial supply: Yes = 90, No = 90
    • A user burns 10 Yes tokens, reducing the Yes supply to 80.
    • The same user mints 20 Yes tokens, bringing the Yes supply back to 100.
    • The final supply becomes: Yes = 100, No = 110, exceeding the cap.

    Recommendation

    Check that the No token supply does not exceed the yesNoTokenCap during minting.

    Resolution

    Trueo Team: Resolved.

  9. M-02 Medium Council Members Cannot Set Challenge Period Access Control Resolved
    Location
    TruthMarketManagerV2.sol: 355-361

    Description

    Inside TruthMarketManagerV2, several functions are available to manage the market's configuration that can be called by the oracle council, such as setYesNoTokenCap, setEndOfTrading, setFirstChallengePeriod, and setSecondChallengePeriod.

    While setYesNoTokenCap and setEndOfTrading are currently available in the OracleCouncilV2 contract, setFirstChallengePeriod and setSecondChallengePeriod are not implemented, preventing council members from configuring those market settings.

    Recommendation

    Implement setFirstChallengePeriod and setSecondChallengePeriod inside OracleCouncilV2.

    Resolution

    Trueo Team: Resolved.

  10. M-03 Medium Possible DOS In _getTotalOpenDisputes DOS Acknowledged
    Location
    OracleCouncilV2.sol: 536

    Description

    The _getTotalOpenDisputes function is used by the addOracleCouncilMember and removeOracleCouncilMember functions in the OracleCouncil contract.

    This function calculates the total open disputes by looping through all active markets and checking if each market is in the DisputeRaised status and has any open disputes. The issue is that _activeMarkets does not have a cap, meaning it could grow indefinitely.

    As a result, the gas costs for calling _getTotalOpenDisputes can become excessively high, potentially exceeding the block gas limit and causing the transaction to fail.

    This could effectively lock the addOracleCouncilMember and removeOracleCouncilMember functions, preventing them from being executed.

    Recommendation

    Introduce a cap on the size of _activeMarkets or consider optimizing the function to avoid iterating through markets that are in a finalized state, as they cannot return to the disputed state.

    Resolution

    Trueo Team: Acknowledged.

  11. M-04 Medium Owner Cannot Trigger disputeMarket Access Control Resolved
    Location
    TruthMarketManagerV2.sol: 303-318

    Description

    Many operations inside TruthMarketManagerV2 that change the market state can be manually triggered by the owner. This is needed in cases where markets are paused, as only the owner can initiate market state transitions, including disputeMarket.

    However, disputeMarket uses the onlyOracleCouncil modifier, which restricts the function to being called only by the oracle council. This is despite the function logic including a check allowing the owner to trigger disputeMarket when markets are paused.

    As a result, disputeMarket cannot be manually called by the owner when markets are paused.

    Recommendation

    Consider to use onlyOracleCouncilAndOwner instead.

    Resolution

    Trueo Team: Resolved.

  12. M-05 Medium Inefficient Distribution In StakingRewards Logical Error Acknowledged
    Location
    StakingRewards.sol

    Description

    Proof of concept: PoC

    If stake is not called in the same block of notifyRewardAmount, depending on delay, a portion of rewards will remain unused inside the contract.

    For example, at time X rewards are transferred into the contract. Then some time Y has passed before the first user stakes. However, the reward period will end at X + rewardsDuration not X + Y + rewardsDuration.

    Therefore, the rewards for Y * rewardRate will remain un-distributed till the next cycle. If a new reward cycle is never started (e.g. final cycle), then any undistributed amount will remain inside the contract.

    Recommendation

    Consider defining periodFinish in the first stake that is done after notifyRewardAmount, when total deposits are zero.

    Resolution

    Trueo Team: Acknowledged.

  13. M-06 Medium Invalid Outcome Voting In voteForDispute DOS Resolved
    Location
    OracleCouncilV2.sol: 187

    Description

    Council members can cast votes referencing an invalid _winningPosition, one not recognized by the market. If enough votes align on this invalid outcome, the dispute remains unresolvable.

    Consequently, it remains open indefinitely, blocking certain system actions (e.g., adding or removing council members) which require all disputes to be closed.

    Recommendation

    Enforce strict _winningPosition validation in voteForDispute, reverting if _winningPosition falls outside 1 to ITruthMarket(_market).positionCount().

    Resolution

    Trueo Team: Resolved.

  14. M-07 Medium OracleBonds Address Change Mishandles Funds Logical Error Partially resolved
    Location
    TruthMarketManagerV2.sol: 496

    Description

    The setOracleBonds function in the TruthMarketManager contract allows the owner to change the oracleBonds address. The problem is that the oracleBonds address is hardcoded into markets when they are created.

    If the address is updated in the TruthMarketManager contract, active markets not yet finalized will send and account for resolve, dispute, and escalate bonds to the new address.

    However, when issuing them back, the old address in the market contract will be used, which will not have the funds or have them accounted for, leading to users losing those funds.

    Furthermore, any unclaimed dispute bonds will also be unclaimable because the OracleCouncil contract will fetch the new address from the TruthMarketManager contract, while these dispute bonds are stored in and accounted for in the old contract.

    Recommendation

    If an oracleBonds update is needed, ensure it is performed only when all active markets are finalized and manually handle users' unclaimed disputed bonds, as the normal functionality will not work for pending disputes before the address change.

    Resolution

    Trueo Team: Resolved.

    Guardian Team: The setOracleBonds function now verifies that all active markets have settled their bonds before an address update is allowed. However, even with this precaution, any unclaimed dispute bonds become unclaimable.

  15. M-08 Medium Lacking Incentives For The Escalator Logical Error Acknowledged
    Location
    TruthMarket.sol: 523-529, TruthMarket.sol: 549-555

    Description

    Currently, if the escalator is correct, the reward is sent to either the resolver or the disputor. This assumes that the escalator is also the resolver or disputor.

    However, considering that the escalator requires a larger bond, it’s possible that the resolver or disputor does not have the required bond, and another party could step in and become the escalator. This third party should be able to receive the reward independently for a correct escalation.

    Recommendation

    Consider splitting the reward between the disputor/resolver and escalator when escalator is correct.

    Resolution

    Trueo Team: Acknowledged.

  16. M-09 Medium Disputer Cannot Re-Dispute After Reset DOS Acknowledged
    Location
    OracleCouncilV2.sol: 419-428

    Description

    When a market is reset following multiple active disputes, a user whose dispute is still considered “open” cannot reclaim their bond because the system only allows bond retrieval after the market is finalized.

    As a result, that user remains in a state with an unclaimed (open) dispute bond, which prevents them from initiating a new dispute on a subsequent outcome proposal. This essentially locks them out of further participation in the dispute process for that market.

    Recommendation

    Enable retrieval or reusability of the bond upon a market reset for disputes that remain open. Specifically, consider allowing users to claim or “migrate” their open dispute bond once the market transitions to a reset state—rather than strictly requiring the market to be finalized.

    Resolution

    Trueo Team: Acknowledged.

  17. M-10 Medium DoS In Dispute Process USDC Is Paused DOS Acknowledged
    Location
    OracleBonds.sol: 301-311

    Description

    The market’s dispute and escalation mechanisms rely on the ability to deposit bonds in USDC. If USDC—being a pausable token—is paused, no new bonds can be deposited, effectively blocking any new disputes or escalations.

    An attacker can exploit this by proposing a resolution in their favor just before USDC becomes paused; if the pause period extends past the challenge window, the outcome remains uncontested, leading to unjust financial gains for the attacker.

    Recommendation

    Extend dispute periods when USDC (or any payment token) is paused until transfers become possible again.

    Resolution

    Trueo Team: Acknowledged.

  18. M-11 Medium Risk Of Re-org Attack Reorg Acknowledged
    Location
    Global

    Description

    In the case of a block re-org event, disputors may unknowingly have submitted disputes for what they believe to be the correct result, leading to slashing and the loss of their disputor bonds.

    For example, consider the following scenario:

    (1) Alice proposes an incorrect resolution.

    (2) Bob disputes Alice’s resolution.

    (3) A block re-org occurs and Alice’s proposal is replaced with a correct resolution.

    (4) Bob’s dispute is invalid and will be punished.

    Recommendation

    Allow disputors to pass the exact position they’d like to dispute to function openDispute.

    Resolution

    Trueo Team: Acknowledged.

  19. M-12 Medium USDC Blacklisted Users Can Freeze Market Transfer Partially resolved
    Location
    TruthMarket.sol

    Description

    If the market is disputed or escalated, it could transfer bonds back to the disputor and escalator if they are not punished. However, since the paymentToken is USDC, which has a blacklist feature, it is possible for the disputor or escalator is blacklisted when the market attempts to return the bonds.

    This would cause the market state transitions to fail. The same issue could occur when attempting to send rewards to the resolver or disputor if the reward token also has a blacklist feature.

    Recommendation

    Consider using the Pull-over-Push pattern when handling bonds and rewards.

    Resolution

    Trueo Team: Resolved.

    Guardian Team: The transfer could still be blocked when sending rewards to a blacklisted address.

  20. M-13 Medium Market DOS From Direct Reset Or Resolve DOS Resolved
    Location
    TruthMarketManagerV2.sol: 257

    Description

    Proof of concept: PoC

    A market can be resolved or reset by the owner directly from the TruthMarketManager contract. One instance where this will be used is, if a market is paused, only the owner can call the reset or resolve market functions instead of following the general flow through either the OracleCouncil contract or the Escalation contract.

    The issue is that when called directly, the dispute or escalation processes are not fully completed as they would be in the general flow. In the dispute case, for instance, one issue would be the marketLastClosedDispute not being updated, meaning the isResolverPunished and isDisputorPunished flags remain unset, defaulting to false.

    Consequently, when the market either resets or finalizes, it will attempt to send funds to the disputer’s address fetching the address from the unset marketLastClosedDispute. Since this address is not set, it would return the zero address, leading to a revert and locking up the market.

    Furthermore, the marketClosedForDisputes is also not set to true as done in the general flow, meaning unclaimed disputes for the market will remain locked.

    Similarly, in the escalation case, if called directly for either reset or resolve, the lack of updates to marketToEscalatedDispute will leave multiple variables such as punishment outcomes empty.

    This will also cause incorrect outcomes, as the punishment outcomes will all default to false and possibly other issues.

    Recommendation

    In the current code, a direct call to reset or resolve the market will cause multiple issues, potentially locking up the market and user funds. Either modify the logic to ensure that if called directly, the dispute or escalation processes are still fully completed, or avoid calling them directly entirely.

    Resolution

    Trueo Team: Resolved.

  21. L-01 Low Markets Can Be Created And Cancelled For Profit Logical Error Acknowledged
    Location
    Global

    Description

    In the current implementation, market creation is restricted to trusted council members. However, once market creation becomes permissionless as per the documentation, it introduces the risk of abuse.

    Malicious actors could create nonsensical or deliberately unresolvable markets and then submit a CANCELED resolution to claim resolver fees

    Recommendation

    To mitigate this risk consider implementing a market creation fee when market creation becomes permissionless.

    Resolution

    Trueo Team: Acknowledged.

  22. L-02 Low Bond Amounts Not Validated Validation Acknowledged
    Location
    TruthMarketManagerV2.sol: 465

    Description

    Within function setAmounts in the TruthMarketManager, is it not validated that escalatorBondAmount is the largest bond amount relative to the disputer and resolver bond amounts.

    According to documentation, "Every escalation must come at an increased cost to that of the previous, so that the protocol may potentially slash the bonds in compensation for the expended resources."

    Recommendation

    Validate the bonds amounts to be according to documentation.

    Resolution

    Trueo Team: Acknowledged.

  23. L-03 Low Market Creation Lacks Input Validation Validation Acknowledged
    Location
    TruthMarketManagerV2.sol: 176

    Description

    Currently, the createMarket function lacks validation for several input fields, which could lead to the creation of invalid or exploitable markets.

    Specifically:

    • rewardToken should be a non-zero address
    • rewardAmount should not be zero and have a minimum amount
    • yesNoTokenCap should be of a reasonable amount

    Recommendation

    Implement validations for the input fields described above.

    Resolution

    Trueo Team: Acknowledged.

  24. L-04 Low Potential Griefing To Mint Transactions Griefing Acknowledged
    Location
    TruthMarket.sol: 303-330

    Description

    The mint function is exposed to a griefing attack where an attacker can frontrun a legitimate user's transaction by minting the exact remaining tokens under the yesNoTokenCap. This causes the legitimate user's transaction to revert due to the TokenCapExceeded() check.

    The attacker can then backrun with a burn transaction, reclaiming the payment tokens spent during the frontrun. Although this attack requires the attacker to have sufficient capital to mint the remaining tokens, it effectively disrupts legitimate users.

    Recommendation

    Introduce a mechanism to limit how quickly addresses can mint, or limit the maximum mint amount per transaction.

    Resolution

    Trueo Team: Acknowledged.

  25. L-05 Low Access Control Naming Convention Best Practices Acknowledged
    Location
    OraclePausable.sol

    Description

    Within OraclePausable, the modifier pauserOnly breaks the typically access control naming convention of onlyRole

    Recommendation

    Consider updating the modifier to onlyPauser.

    Resolution

    Trueo Team: Acknowledged.

  26. L-06 Low Incorrect TrueToken maxSupply Logical Error Acknowledged
    Location
    TrueToken.sol: 11

    Description

    The Truemarket documentation states that the total supply of TRUE is 100 million. However, the current maxSupply set in the TrueToken contract is 1 billion, which contradicts the documentation.

    Recommendation

    Update the maxSupply value in the TrueToken contract to align with the documented total supply of 100 million.

    Resolution

    Trueo Team: Acknowledged.

  27. L-07 Low Block In _isValidTransition Never Triggered Logical Error Acknowledged
    Location
    TruthMarket.sol: 600

    Description

    In the _isValidTransition function, the else block for handling transitions when the from status is ResetByCouncil is never triggered.

    This is due to the behavior of getCurrentStatus, which automatically transitions the ResetByCouncil status to OpenForResolution if the current timestamp is past the second challenge period.

    Furthermore, the logic in the else block is incorrect because, there is no direct transition from ResetByCouncil to OpenForResolution.

    Recommendation

    Consider removing the else block.

    Resolution

    Trueo Team: Acknowledged.

  28. L-08 Low Token Loss Via Truncation In Burn() Function Truncation Acknowledged
    Location
    TruthMarket.sol: 324-330

    Description

    When amount is multiplied by 10^paymentTokenDecimals and then divided by 10^tokenDecimals, any amount smaller than the ratio 10^(tokenDecimals - paymentTokenDecimals) is rounded down to 0.

    As a result, users may burn their YES/NO tokens but receive zero payment tokens, causing them to lose tokens unintentionally.

    Recommendation

    Require paymentTokenAmount > 0 before executing the burn, it's also recommended to only burn a multiple of 10^(tokenDecimals - paymentTokenDecimals) from the users token avoiding loss from their end due to truncation.

    Resolution

    Trueo Team: Acknowledged.

  29. L-09 Low Clear Data After Removing Council Member Logical Error Acknowledged
    Location
    OracleCouncilV2.sol: 136-146

    Description

    When removeOracleCouncilMember is called, it moves councilMemberAddress at councilMemberCount to councilMemberIndex[_councilMember], but the councilMemberAddress at councilMemberCount is not cleared.

    Recommendation

    When removing a council member, consider clearing councilMemberAddress at councilMemberCount.

    Resolution

    Trueo Team: Acknowledged.

  30. L-10 Low Mismatched Event Parameter Order Events Acknowledged
    Location
    TruthMarketManagerV2.sol: 396-404

    Description

    In the setAddresses function, the AddressesUpdated event is emitted with a specific parameter order. However, the actual arguments passed do not match this order.

    This causes misalignment between the event’s named parameters and the actual addresses being updated, leading to potential confusion or incorrect off-chain tracking.

    Recommendation

    Reorder the arguments in the emit AddressesUpdated(...) call to match the event’s parameter list or update the event definition to align with the actual argument order.

    Resolution

    Trueo Team: Acknowledged.

  31. L-11 Low Clear Data After Removing Pauser Logical Error Acknowledged
    Location
    TruthMarketManagerV2.sol: 526-538

    Description

    When removePauserAddress is called, it moves pauserAddress at pauserCount to pauserIndex[_pauserAddress], but the pauserAddress at pauserCount is not cleared.

    Recommendation

    When a pauser is removed, consider clearing pauserAddress at pauserCount

    Resolution

    Trueo Team: Acknowledged.

  32. L-12 Low OracleCouncil Can't Pause/Unpause MarketManager Logical Error Acknowledged
    Location
    TruthMarketManagerV2.sol: 540

    Description

    The TruthMarketManager contract uses the onlyOracleCouncilAndOwner modifier on the pause and unpause functions, allowing the owner and OracleCouncil address to call them.

    However, the OracleCouncil contract does not implement the functionality to invoke these functions, preventing it from pausing or unpausing the TruthMarketManager contract as intended.

    Recommendation

    Implement the necessary functionality in the OracleCouncil contract to allow it to call the pause and unpause functions in the TruthMarketManager contract.

    Resolution

    Trueo Team: Acknowledged.

  33. L-13 Low setEndOfTrading Lacks Input Validation Validation Acknowledged
    Location
    TruthMarketManagerV2.sol: 351-353

    Description

    When a market is newly created, it ensures that _endOfTrading is greater than minimumTradingDuration. However, when setEndOfTrading is called, there is no validation, making it possible to configure the market with an endOfTrading that is less than minimumTradingDuration.

    Recommendation

    Add the same validation inside setEndOfTrading.

    Resolution

    Trueo Team: Acknowledged.

  34. L-14 Low Missing setPaused Function In Market Manager Logical Error Acknowledged
    Location
    TruthMarketManagerV2.sol

    Description

    In OraclePausable, setPaused can be called by owner() which is the TruthMarketManagerV2 contract. However, this function is not implemented in TruthMarketManagerV2.

    Recommendation

    Consider implementing setPaused in TruthMarketManagerV2.

    Resolution

    Trueo Team: Acknowledged.

  35. L-15 Low Incorrect createdAt Timestamp In Escalations Logical Error Acknowledged
    Location
    Escalation.sol: 136

    Description

    When a new dispute is opened through the openEscalatedDispute function, the dispute’s createdAt is set to block.timestamp.

    However, this value is overwritten when the setEscalationProposalId function is called, which sets createdAt to block.timestamp again. As a result, the createdAt variable will not accurately reflect the original time the dispute was created.

    Recommendation

    Ensure that the createdAt variable is only set once when the dispute is initially opened through the openEscalatedDispute.

    Resolution

    Trueo Team: Acknowledged.

  36. L-16 Low resolveEscalatedDispute Lacks Input Validation Validation Acknowledged
    Location
    Escalation.sol

    Description

    In the function resolveEscalatedDispute, the input arguments should be validated to avoid any illogical state.

    Notably:

    • _isResultReset and _isResultAccept should never be equal
    • Punish arguments (x3) should never all be true.

    Additionally, isEscalatedDisputorPunished should be set to false when _isResultAccept is true - as the escalator should never be punished if the escalation was accepted.

    Similarly, isCouncilDisputorPunished should be set to false when _isResultAccept is false. Punish arguments should be validated to ensure they do not conflict with this.

    Recommendation

    Add the recommended validations to resolveEscalatedDispute.

    Resolution

    Trueo Team: Acknowledged.

  37. L-17 Low Disputes Allow Potential Abuse Validation Acknowledged
    Location
    OracleCouncilV2.sol: 148

    Description

    Users can open a dispute for a market once a resolution is proposed by calling the openDispute function with the market address and _disputeString as parameters. The _disputeString allows users to explain the reason for the dispute and present evidence.

    However, with no restrictions or validations on the _disputeString, malicious users could exploit this feature to post malicious or misleading links. If these links are displayed on the front end, they could lead unsuspecting users to phishing sites or other malicious content.

    Additionally, since only one dispute can be processed and rewarded or punished, the cost of such an attack is limited to the disputer bond amount. A malicious actor could open multiple disputes with different dispute strings to increase the chances of unsuspecting users clicking on the harmful links.

    Recommendation

    Ensure that disputes displayed on the front end are validated to prevent malicious content from being presented to users.

    Resolution

    Trueo Team: Acknowledged.

  38. L-18 Low Reset By Council Cannot Be Escalated Logical Error Acknowledged
    Location
    Trueo.sol: 228

    Description

    When the council decides to reset a market with _returnToOpenForResolution == true, market status is immediately set to OpenForResolution.

    As a result, the decision cannot be disputed by escalation which prevents a confident resolver from disputing the decision, so as not to lose the potential reward.

    Recommendation

    Consider if this is expected. Otherwise, allow for dispute by escalation, even if the council's decision is to reset and open market for resolution.

    Resolution

    Trueo Team: Acknowledged.

  39. L-19 Low Max Council Members Can Be Violated Logical Error Acknowledged
    Location
    OracleCouncilV2.sol: 125

    Description

    The addOracleCouncilMember function incorrectly implements the check for the maximum number of council members, allowing the limit to be exceeded by one.

    The condition:

    if (councilMemberCount > marketManager.maxOracleCouncilMembers()) revert MaxOracleCouncilMembersExceeded(); does not prevent adding an 11th member when the maximum allowed is 10.

    This happens because the check is only triggered after the count exceeds the limit, rather than when it reaches the limit.

    Recommendation

    Update the condition to ensure the council member count does not exceed the maximum:

    if (councilMemberCount = marketManager.maxOracleCouncilMembers()) revert MaxOracleCouncilMembersExceeded();

    Resolution

    Trueo Team: Acknowledged.

Invariants 17

The review's fuzzing suite asserted 17 invariants. 13 held and 4 did not.

Every invariant tested
IDInvariantResult
ERR-01Only protocol errors allowedHeld
GLOB-00Market status order violatedBroken
GLOB-01After a market reaches status Finalized, it's status should never change.Held
GLOB-02Should not see any ERC20InsuffcientBalance reverts (specifically from the contract, actor balances should be handled with a sufficientHeld
GLOB-03mint and handler pre-conditions) If MarketStatus == ResetByCouncil then oracleCouncil.getLastClosedDispute(address(tHeld
GLOB-04his)).lastDisputorAddress != address(0) TruthMarket should only hold a rewardToken balance of rewardAmount or 0Held
GLOB-05Payment token balance inside the market should always be enough to cover all user'sHeld
GLOB-06burn under all market conditions. EscalatedDisputerBondAmount should be equal to disputerBondAmountHeld
GLOB-07DisputorTotalBond should be equal to disputorsCount * disputerBondAmountHeld
GLOB-08Resolution proposed before secondChallengePeriod elapsed afterBroken
DIS-01ResetByCouncil OracleCouncil.voteForDispute - disputeVotesCount should never change if the council member had a prior vote and isHeld
RES-01changing their vote. After a proposeResolution function call the marketBond[_market].totalMarketBond should increase by theBroken
RES-02ITruthMarket(_marketAddress).resolverBondA mount() After a proposeResolution function call the marketBond[_market].totalDepositedMarketBo nd should increase by theBroken
RES-03ITruthMarket(_marketAddress).resolverBondA mount() OracleBonds tracking of each market's resolverBond balance should never exceed resolverBond amount. Any violation wouldHeld
RES-04imply multiple resolutions proposed or failure to refund bond during reset. OracleBonds tracking of each market's escalatedDisputorBond balance should never exceed escalatorBondAmount amount. AnyHeld
REED-01violation would imply multiple escalations possible. Payment token balance inside the market should always be enough to cover all user's redeem when market is finalized and winningHeld
WFCM-01position is either YES or NO. Payment token balance inside the market should always be enough to cover all user's withdrawFromCanceledMarket when market is finalized and winning position is CANCELED.Held

More from Trueo

  1. Staking Updates

    68 findings4 high 68 findings: 4 high, 22 medium, 23 low, 19 informational
  2. Uniswap V4

    37 findings8 high 37 findings: 8 high, 15 medium, 10 low, 4 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote