Oracle: Call any API from a smart contract
An oracle is a midpoint that makes HTTP requests from contracts to off-chain endpoints. Unlike traditional oracle networks - midpoints allow developers to define arbitrarily complex requests with components such as parameters/headers defined on-chain, multistep authentication workflows, private off-chain data for keys, and cross-chain functionality.
We will create a contract to request a summary of any Wikipedia article. We will use the Startpoint Called source, the Make HTTP Request task, and the Transact to EVM Function task.
StartpointCalledSource:
- 1.Listens for the caller to send the title of a wikipedia article
- 2.Extracts the title and sends it to MakeHttpRequestTask
MakeHttpRequestTask:
- 1.Makes a request to the Wikimedia API
- 2.Extracts the summary from the HTTP response
- 3.Sends the summary to the TranasctToEvmFunctionTask
TransactToEvmFunctionTask:
- 1.Submits the summary via a transaction to an on-chain function defined by the developer
$ npm install -g midpoint-cli
$ midpoint init blank my-oracle
$ midpoint add-source startpointCalledSource oracle-source
{
"startpointCalledSource": {
"whitelist": [
{
"chainId": "5",
"contractAddress": "0xDDb539E3c742f3DFA8eB3cf0f7122214f805B962"
}
],
"variables": [
{
"name": "article",
"datatype": "string"
}
],
"extracts": [
{
"name": "Midpoint_ID",
"from": "Midpoint_ID"
},
{
"name": "Request_ID",
"from": "Request_ID"
}
]
}
}
2. MakeHTTPRequestTask
$ midpoint add-task makeHttpRequest call-wikipedia
{
"makeHttpRequest": {
"method": "GET",
"urlRaw": "https://en.wikipedia.org/api/rest_v1/page/summary/{{article}}",
"urlType": "raw",
"extracts": [
{
"name": "wikipediaSummary",
"from": "body.extract"
}
]
}
}
$ midpoint add-task transactToEvmFunctionTestnet callback
{
"transactToEvmFunctionTestnet":{
"chainId":"5",
"contractAddress":"0xDDb539E3c742f3DFA8eB3cf0f7122214f805B962",
"functionName":"requestedWikipediaSummary",
"arguments":[
{
"name":"midpointId",
"datatype":"uint64",
"value":"{{Midpoint_ID}}"
},
{
"name":"requestId",
"datatype":"uint64",
"value":"{{Request_ID}}"
},
{
"name":"wikipediaSummary",
"datatype":"string",
"value":"{{wikipediaSummary}}"
}
]
}
}
We define our path file.
oracle-source => call-wikipedia
call-wikipedia => callback
/*
* SPDX-License-Identifier: MIT
* Midpoint Sample Storage Contract v3.0.0
*
* This is a contract generated at 2023-02-28 20:07:34 for testing requests to midpoint 160.
* This contract is intended to serve as a guide for interfacing with a midpoint and should not be used
* as is in a production environment.
* For more information on setting up a midpoint and using this contract see docs.midpointapi.com
*/
pragma solidity>=0.8.0;
interface IMidpoint {
function callMidpoint(uint64 midpointId, bytes calldata _data) external returns(uint64 requestId);
}
contract TestMidpointOracleContract {
// These events can be removed without impacting the functionality of your midpoint
event RequestForWikipediaSummaryMade(string article);
event WikipediaSummaryReceived(uint64 Request_ID, uint64 Midpoint_ID, string wikipedia_summary);
// A verified startpoint for Goerli Testnet
address constant startpointAddress = 0x795c5292b9630d473d568079b73850F29344403c;
// A verified midpoint transact to EVM function EOA for Goerli Testnet
address constant whitelistedCallbackAddress = 0xC0FFEE4a3A2D488B138d090b8112875B90b5e6D9;
// The globally unique identifier for your midpoint
uint64 constant midpointID = 160;
// Mapping of Request ID to a flag that is checked when the request is satisfied
// This can be removed without impacting the functionality of your midpoint
mapping(uint64 => bool) public request_id_satisfied;
// Mappings from Request ID to each of your results
// This can be removed without impacting the functionality of your midpoint
mapping(uint64 => string) public request_id_to_wikipedia_summary;
/*
* This function makes a call to your midpoint with On-Chain Variables specified as function inputs.
*
* Note that this is a public function and will allow any address or contract to call midpoint 160.
* Configure your midpoint to permit calls from this contract when testing. Before using your midpoint
* in a production environment, ensure that calls to 'callMidpoint' are protected.
* Any call to 'callMidpoint' from a whitelisted contract will make a call to your midpoint;
* there may be multiple places in this contract that call the midpoint or multiple midpoints called by the same contract.
*/
function getWikipediaSummary(string memory article) public {
// This packs together all of the On-Chain Variables for your midpoint into a single bytestring
bytes memory args = abi.encodePacked(article, bytes1(0x00));
// This makes the call to your midpoint
uint64 requestId = IMidpoint(startpointAddress).callMidpoint(midpointID, args);
// This logs that the call has been made, and can be removed without impacting your midpoint
emit RequestForWikipediaSummaryMade(article);
request_id_satisfied[requestId] = false;
}
/*
* This function is the callback target specified in your midpoint callback definition.
* Note that the callback is placed in the same contract as the call to callMidpoint for simplicity when testing.
* The callback does not need to be defined in the same contract as the request or live on the same chain.
*/
function requestedWikipediaSummary(uint64 midpointId, uint64 requestId, string memory wikipediaSummary) public {
// Only allow a verified callback address to submit information for your midpoint.
require(tx.origin == whitelistedCallbackAddress, "Invalid callback address");
// Only allow requests that came from your midpoint ID
require(midpointID == midpointId, "Invalid Midpoint ID");
// This stores each of your response variables. This is where you would place any logic associated with your callback.
// Your midpoint can transact to a callback with arbitrary execution and gas cost.
request_id_to_wikipedia_summary[requestId] = wikipediaSummary;
// This logs that a response has been received, and can be removed without impacting your midpoint
emit WikipediaSummaryReceived(requestId, midpointId, wikipediaSummary);
request_id_satisfied[requestId] = true;
}
}
$ midpoint publish