hats-finance / SeeR-PM-0x899bc13919880db76edf4ccd72bdfa5dfa666fb7

1 stars 0 forks source link

RealityProxy does not convert the bytes32 answer format to payout format #29

Open hats-bug-reporter[bot] opened 4 hours ago

hats-bug-reporter[bot] commented 4 hours ago

Github username: -- Twitter username: -- Submission hash (on-chain): 0x4a2d4ff3fbd671921a0da95770db945ae697bd2e43e74f4ab71ce2aaf568af79 Severity: high

Description: Description\

In RealityProxy, the contract is used to resolve the answer.

In ResolveMultiScalarMarket, the code does not convert the answer to payout

   function resolveMultiScalarMarket(
        bytes32 questionId,
        bytes32[] memory questionsIds,
        uint256 numOutcomes
    ) internal {
        uint256[] memory payouts = new uint256[](numOutcomes + 1);
        bool allZeroesOrInvalid = true;

        /*
         * We set maxPayout to a sufficiently large number for most possible outcomes that also avoids overflows in the following places:
         * https://github.com/gnosis/conditional-tokens-contracts/blob/master/contracts/ConditionalTokens.sol#L89
         * https://github.com/gnosis/conditional-tokens-contracts/blob/master/contracts/ConditionalTokens.sol#L242
         */
        uint256 maxPayout = 2 ** (256 / 2) - 1;

        for (uint256 i = 0; i < numOutcomes; i++) {
            payouts[i] = uint256(realitio.resultForOnceSettled(questionsIds[i]));

            if (payouts[i] == uint256(INVALID_RESULT)) {
                payouts[i] = 0;
            } else if (payouts[i] > maxPayout) {
                payouts[i] = maxPayout;
            }

            allZeroesOrInvalid = allZeroesOrInvalid && payouts[i] == 0;
        }

        if (allZeroesOrInvalid) {
            // invalid result.
            payouts[numOutcomes] = 1;
        }

        conditionalTokens.reportPayouts(questionId, payouts);
    }

https://gnosisscan.io/address/0xE78996A233895bE74a66F451f1019cA9734205cc#code#L607

as we can see, the code use realitio.resultForOnceSettled,

but the code return answer formatted as a bytes32

 /// @notice Return the final answer to the specified question, or revert if there isn't one
    /// @param question_id The ID of the question
    /// @return The answer formatted as a bytes32
    function resultFor(bytes32 question_id) 
        stateFinalized(question_id)
    public view returns (bytes32) {
        return questions[question_id].best_answer;
    }

Attack Scenario\

Code should convert the best answer bytes32 format to payout format

Attachments

  1. Proof of Concept (PoC) File

  2. Revised Code File (Optional)

greenlucid commented 44 minutes ago
payouts[i] = uint256(realitio.resultForOnceSettled(questionsIds[i]));

is converting that bytes32 to uint256, the "payout format" you're referring to. see the cast