UNPKG

@josojo/realitycheck-contracts

Version:

RealityCheck demo

226 lines (148 loc) 8.4 kB
Maybe you want to turn your Nazi parade into an anti-racist walkathon You can also use it as a fallback if an automated check fails. eg flight stats discover the violation of their terms of service, or oraclize lose their private key. question id options: * always use calling address, plus arbitrary 32 bytes Using it in contracts Creating a question: - save your question on IPFS. - Getting an answer from a contract First, we create a question in Reality Check. You can do this on the website. We send the ID of question, and the address of the Reality Check contract, to our constructor. We also send it the other settings for our walkathon: The charity that will get money for each Nazi, and the amount (in Wei) it will get per Nazi. contract NaziWalkathon { address realitycheck; address owner; address anti_racist_charity; uint256 wei_per_nazi; address question_id; function NaziWalkathon(address _realitycheck, address _question_id, address _charity, uint256 _wei_per_nazi) { realitycheck = _realitycheck; question_id = _question_id; anti_racist_charity = _charity; wei_per_nazi = _wei_per_nazi; owner = msg.sender; } function() payable {} function claim() { // We only want this function to be successfully called once. // So we'll check it hasn't been zeroed, and zero it once we've finished with it. require(question_id != 0x0); // Get the number of nazis from the RealityCheck contract. // This will throw an error if RealityCheck does not yet have a final answer. // RealityCheck always returns bytes32 data. // In this case it is a number, so we cast it to uint256. uint256 number_of_nazis = uint256(realitycheck.getFinalAnswer(question_id)); question_id = 0x0; uint256 wei_to_charity = number_of_nazis * wei_per_nazi; uint256 wei_to_owner = 0; if (wei_to_charity >= address.balance) { // There were many nazis that all our money goes to charity wei_to_charity = address.balance; } else { // There weren't too many nazis, so the owner gets some money back wei_to_owner = address.balance - wei_to_charity; } if (wei_to_charity > 0) { anti_racist_charity.transfer(to_charity); } if (wei_to_owner > 0) { owner.transfer(to_charity); } } } Getting the answer via a callback The above requires someone to send a transaction to claim your funds from the contract after the result is known. This is often the correct design. However, you may sometimes want somebody to call your contract for you, so you don't need to bother. This is the pattern that Oraclize use. You can do that too, but instead of having a trusted party call your contract, we let anyone call your contract via the RealityCheck contract. First, when we get paid, let's request a callback. function() payable { uint256 needed_gas = 100000; uint256 tip = 100000000000; // todo check reasonable number uint256 gas_price = tx.gasprice; realitycheck.fundCallbackRequest.value(needed_gas * gas_price + tip)(question_id, this, 100000); } // Now we replace our "claim" function with a function called __factcheck_callback. function __factcheck_callback(bytes32 question_id, bytes32 number_of_nazis) { // We only want this function to be successfully called once. require(question_id != 0x0); question_id = 0x0; require(msg.sender == realitycheck); uint256 wei_to_charity = number_of_nazis * wei_per_nazi; uint256 wei_to_owner = 0; if (wei_to_charity >= address.balance) { // There were many nazis that all our money goes to charity wei_to_charity = address.balance; } else { // There weren't too many nazis, so the owner gets some money back wei_to_owner = address.balance - wei_to_charity; } if (wei_to_charity > 0) { anti_racist_charity.transfer(to_charity); } if (wei_to_owner > 0) { owner.transfer(to_charity); } } } Make sure you send enough money to make it worth their while for somebody to send the callback. Bear in mind that the price of gas may go up between the time you fund the bounty and the time an answer to the question is available, which will make it less profitable for someone to do it. If you find you haven't sent enough money, all is not lost. You can always add some more money, or you can call the callback yourself directly against the Reality Check contract: realitycheck.sendCallback(question_id, your_contract, needed_gas, 0); * Asking a question In the previous examples, we created the question ourselves, then put the question ID in our own contract. But we can also create the question with our own contract. Instead of sending our contract a question ID, we send it the parameters needed to register the question with RealityCheck: * IPFS address of the question, * The timeout, which is how long someone will have to create a new answer * address of the arbitrator. bytes32 question_id; function NaziWalkathon(address _realitycheck, bytes32 _question_ipfs, address arbitrator, uint256 , address _charity, uint256 _wei_per_nazi) { realitycheck = _realitycheck; question_id = _question_id; anti_racist_charity = _charity; wei_per_nazi = _wei_per_nazi; owner = msg.sender; uint256 = 1 day; question_id = realitycheck.askQuestion(question_ipfs, , arbitrator, 0); } Why would you want to register the question with your just registering the question with our website and sending the resulting ID to your contract? One reason might be because you want control of the timing of the registration of the question. You want to set up your contract before the walkathon takes place, but you don't want the question answered too early; If somebody asks the question before the march happens, the only way to pause it until the march actually happens will be to request arbitration, which is expensive. So let's change our walkathon contract so that nobody can register the question until the day after the march. function registerQuestion() { require(now > 1492646400); question_id = realitycheck.askQuestion(question_ipfs, , arbitrator, 0); } * Formatting the results As far as the contract is concerned, the answer to a question is 32 bytes of data. The meaning of this data is left entirely to humans, as expressed in the JSON file you store on IPFS. Our web application is designed to expect the following: * Free text on IPFS: An IPFS document. * Number * Single choice * Multiple choice A contract can easily cast a bytes32 into a number. uint256 my_result = realitycheck.getFinalAnswer(question_id); * Dealing with questions that don't have clear answers Sometimes a question doesn't yet have an answer. Sometimes a question is ambiguous. Sometimes a question has an answer, but we're not yet sure what it is. The way you want to handle this will depend on your situation. You can specify the behaviour you want in your question. For example, you may want the contract to answer with "Don't know" or "Not known yet", using a particular preassigned code as the response bytes32. * Minimum answering dates You may want to create a question that can't be answered before a certain date. For instance, if you want the exchange rate of ETH to USD on 2018-01-01, you don't want to have to monitor that question until then. You can do this with a wrapper contract that will not allow the question to be asked until that date. contract FutureQuestion { bytes32 question_ipfs = 0xdeadbeef; uint256 = 1 day; address arbitrator = 0xbabebabe; bytes32 question_id; function askQuestion() { require(now < 1514764800); bytes32 our_code = question_ipfs; bytes32 question_id = realitycheck.askQuestion(question_ipfs, , arbitrator, our_code); } } The question_id is predictable: It's the keccak256 hash of the address of your contract, plus the code assigned by your contract. This means you can refer to it in other contracts, before the question has actually been registered with the RealityCheck contract. * Getting an answer if and when a question has one. For example, maybe you want to know when there is an earthquake in Tokyo. For all we know there could have been an earthquake in Tokyo at any time, and any time somebody claims there has been one, somebody will have to check. bytes32 NULL_ANSWER = 0x0;