Description:Description\
The contract MarketFactory has four functions for market creation, namely: createCategoricalMarket , createMultiCategoricalMarket , createScalarMarket and createMultiScalarMarket . These methods are not payable , which means the caller cannot send native currency when calling them. Although, these methods make use of external functions that are payable and have proper checks in place that will revert if the necessary msg.value is not sent.
Reproduce Scenario
Call createMultiScalarMarket() function
internal function createMarket() is called
internal function createNewMarketParams() is called
internal function askRealityQuestion() is called
external payable function askQuestionWithMinBond() is called
external’s contract _askQuestion() function is called
checks whether the msg.value is greater or equal to question_fee
Attachments
Proof of Concept (PoC)
function createMultiScalarMarket(CreateMarketParams calldata params) external returns (address) {
...
function askRealityQuestion(
string memory encodedQuestion,
uint256 templateId,
uint32 openingTime,
uint256 minBond
) internal returns (bytes32) {
bytes32 content_hash = keccak256(abi.encodePacked(templateId, openingTime, encodedQuestion));
bytes32 question_id = keccak256(
abi.encodePacked(
content_hash, arbitrator, questionTimeout, minBond, address(realitio), address(this), uint256(0)
)
);
if (realitio.getTimeout(question_id) != 0) {
return question_id;
}
return realitio.askQuestionWithMinBond(
templateId, encodedQuestion, arbitrator, questionTimeout, openingTime, 0, minBond
);
}
...
function askQuestionWithMinBond(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond)
// stateNotCreated is enforced by the internal _askQuestion
public payable returns (bytes32) {
require(templates[template_id] > 0, "template must exist");
bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, min_bond, address(this), msg.sender, nonce));
// We emit this event here because _askQuestion doesn't need to know the unhashed question.
// Other events are emitted by _askQuestion.
emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, block.timestamp);
_askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, min_bond);
return question_id;
}
...
function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 min_bond)
stateNotCreated(question_id)
internal {
// A timeout of 0 makes no sense, and we will use this to check existence
require(timeout > 0, "timeout must be positive");
require(timeout < 365 days, "timeout must be less than 365 days");
uint256 bounty = msg.value;
// The arbitrator can set a fee for asking a question.
// This is intended as an anti-spam defence.
// The fee is waived if the arbitrator is asking the question.
// This allows them to set an impossibly high fee and make users proxy the question through them.
// This would allow more sophisticated pricing, question whitelisting etc.
if (arbitrator != NULL_ADDRESS && msg.sender != arbitrator) {
uint256 question_fee = arbitrator_question_fees[arbitrator];
require(bounty >= question_fee, "ETH provided must cover question fee");
bounty = bounty - question_fee;
balanceOf[arbitrator] = balanceOf[arbitrator] + question_fee;
}
...
}
Revised Code
In order to overcome this issue, add the payable keyword to every market creation function within MarketFactory contract.
None of those function receive nor send any base assets. Reality has some option to provide some question bounty (I can't see it ever been used) which is not used by Seer either.
Github username: -- Twitter username: -- Submission hash (on-chain): 0xa12d42f476f20830a264cbc23a927d34e2ef13fd1e0364ee588147dbb15ca3be Severity: medium
Description: Description\ The contract
MarketFactory
has four functions for market creation, namely:createCategoricalMarket
,createMultiCategoricalMarket
,createScalarMarket
andcreateMultiScalarMarket
. These methods are notpayable
, which means the caller cannot send native currency when calling them. Although, these methods make use of external functions that arepayable
and have proper checks in place that will revert if the necessarymsg.value
is not sent.Reproduce Scenario
Call
createMultiScalarMarket()
functioninternal function
createMarket()
is calledinternal function
createNewMarketParams()
is calledinternal function
askRealityQuestion()
is calledexternal payable function
askQuestionWithMinBond()
is calledexternal’s contract
_askQuestion()
function is calledchecks whether the
msg.value
is greater or equal toquestion_fee
Attachments
In order to overcome this issue, add the
payable
keyword to every market creation function withinMarketFactory
contract.