Request-Response
Arbitrary Off-Chain Data Available To Your Smart Contract
A detailed example of how to use the Orakl Network Request-Response can be found at example repository request-response-consumer
.
What is Request-Response?
The Orakl Network Request-Response serves as a solution to cover a wide range of use cases. While it may not be possible to bring every data feed directly to the blockchain, the Request-Response allows users to specify within their smart contracts the specific data they require and how they should be processed before they are received on-chain. This feature returns data in Single Word Response format, providing users with greater flexibility and control over their data, and allowing them to access a wide range of external data sources.
Orakl Network Request-Response can be used with two different account types that support prepayment method:
Permanent Account allows consumers to prepay for Request-Response services, and then use those funds when interacting with Orakl Network. Permanent account is currently a recommended way to request for Request-Response. You can learn more about prepayment payment method or permanent account, go to developer's guide on how to use Prepayment.
Temporary Account allows user to pay directly for Request-Response without any extra prerequisites. This approach is great for infrequent use, or for users that do not want to hassle with Temporary Account settings and want to use Request-Response as soon as possible.
In this document, we describe both Permanent Account and Temporary Account approaches that can be used for requesting data from off-chain. Finally, we explain how to build an on-chain requests and how to post-process an API response.
Permanent Account (recommended)
We assume that at this point you have already created permanent account through Prepayment
smart contract, deposited $KAIA, and assigned consumer(s) to it. If not, please read how to do all the above, in order to be able to continue in this guide.
After you created account (and obtained accId
), deposited some $KAIA and assigned at least one consumer, you can use it to request data and receive response.
User smart contract that wants to utilize Orakl Network Request-Response has to inherit from abstract fulfillment contracts to support a specific return data type. Currently, we provide the following:
uint128
withRequestResponseConsumerFulfillUint128
int256
withRequestResponseConsumerFulfillInt256
bool
withRequestResponseConsumerFulfillBool
string
withRequestResponseConsumerFulfillString
bytes32
withRequestResponseConsumerFulfillBytes32
bytes
withRequestResponseConsumerFulfillBytes
All of the above are defined within a RequestResponseConsumerFulfill file. For the sake of this tutorial, we will demonstrate with RequestResponseConsumerFulfillUint128
only, but the same principles can be applied to other return data types.
Initialization
Request-Response smart contract (RequestResponseCoordinator
) is used both for requesting and receiving data. Address of deployed RequestResponseCoordinator
is used for initialization of parent class RequestResponseConsumerBase
.
Get estimated service fee
The estimateFee
function calculates the estimated service fee for a request based on the provided parameters.
Let's understand the purpose and arguments of this function:
reqCount
: This is auint64
value representing the number of previous requests made. By providing theaccId
, you can obtain thereqCount
by invoking the external functiongetReqCount()
of thePrepayment contract
numSubmission
: This is auint8
value representing the number of submissions for the request.callbackGasLimit
: This is auint32
value representing the gas limit allocated for the callback function.
By calling the estimateFee()
function with the appropriate arguments, users can get an estimation of the total fee required for their request. This can be useful for spending required amount for each request.
Request data
Data request (requestData
) must be called from a contract that has been approved through addConsumer
function of Prepayment
smart contract. If the smart contract has not been approved, the request is rejected with InvalidConsumer
error. If account (specified by accId
) does not exist (InvalidAccount
error) or does not have balance high enough, request is rejected as well.
The example code below encodes a request for an ETH/USD price feed from https://min-api.cryptocompare.com/ API server. The request describes where to fetch data (https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD), and how to parse (path
and pow10
) the response from API server (shortened version displayed in listing below). The response comes as a nested JSON dictionary on which we want to access RAW
key at first, then ETH
key, USD
key, and finally PRICE
key.
After accessing the ETH/USD price, we notice that the price value is encoded in floating point. To simplify transition of floating point value from off-chain to on-chain, we decide to multiply the price value by 10e8 and keep the value in uint256
data type. This final value is submitted by the off-chain oracle to RequestResponseCoordinator
which consequently calls fulfillDataRequest
function in your consumer smart contract. In the final section of this page, you can learn more about other ways how to build a request and how to parse the response from off-chain API server.
Below, you can find an explanation of requestData
function and its arguments defined at RequestResponseCoordinator
smart contract:
req
: aRequest
structure that holds encoded user requestcallbackGasLimit
: auint32
value representing the gas limit for the callback function that executes after the confirmations have been received.accId
: auint64
value representing the ID of the account associated with the request.numSubmission
: requested number of submission from off-chain oracles
The function call requestData()
on COORDINATOR
contract passes req
, accId
, callbackGasLimit
and numSubmission
as arguments. After a successful execution of this function, you obtain an ID (requestId
) that uniquely defines your request. Later, when your request is fulfilled, the ID (requestId
) is supplied together with response to be able to make a match between requests and fulfillments when there is more than one request.
Receive response
fulfillDataRequest
is a virtual function of RequestResponseConsumerFulfillUint128
abstract smart contract (every RequestResponseConsumerFulfill*
defines this function), and therefore must be overridden. This function is called by RequestResponseCoordinator
when fulfilling the request. callbackGasLimit
parameter defined during data request denotes the amount of gas required for execution of this function.
The arguments of fulfillDataRequest
function are explained below:
requestId
: auint256
value representing the ID of the requestresponse
: anuint128
value that was obtained after processing data request sent fromrequestData
function
This function is executed from RequestResponseCoordinator
contract defined during smart contract initialization. The result is saved in the storage variable sResponse
.
Temporary Account
Temporary Account is an alternative type of account which does not require a user to create account, deposit $KAIA, or assign consumer before being able to utilize Request-Response functionality. Request-Response with Temporary Account is only a little bit different compared to Permanent Account, however, the fulfillment function is exactly same.
User smart contract that wants to utilize Orakl Network Request-Response has to inherit from one of the following abstract smart contracts that define what data type we expect as a response.
uint128
withRequestResponseConsumerFulfillUint128
int256
withRequestResponseConsumerFulfillInt256
bool
withRequestResponseConsumerFulfillBool
string
withRequestResponseConsumerFulfillString
bytes32
withRequestResponseConsumerFulfillBytes32
bytes
withRequestResponseConsumerFulfillBytes
Initialization with Temporary Account
There is no difference in initialization of Request-Response consumer contract that requests for data with permanent or temporary account.
Request-Response smart contract (RequestResponseCoordinator
) is used both for requesting and receiving data. Address of deployed RequestResponseCoordinator
is used for initialization of parent class RequestResponseConsumerBase
.
Request data with Temporary Account (consumer)
The data request using Temporary Account is very similar to request using Permanent Account. The only difference is that for Temporary Account user has to send $KAIA together with call using value
property, and does not have to specify account ID (accId
) as in Permanent Account. There are several checks that have to pass in order to successfully request data. You can read about them in one of the previous subsections called Request data.
This function calls the requestData()
function defined in COORDINATOR
contract, and passes req
, callbackGasLimit
, numSubmission
and refundRecipient
as arguments. The payment for service is sent through msg.value
to the requestData()
in COORDINATOR
contract. If the payment is larger than expected, the exceeding amount is returned to the refundRecipient
address. Eventually, it generates a data request.
In the section below, you can find more detailed explanation of how data request using temporary account works.
Cancel request and receive refund (consumer)
In the previous section, we explained that $KAIA is sent together with request for data to RequestResponseCoordinator
which passes the $KAIA deposit to Prepayment
contract. The $KAIA payment stays in the Prepayment
contract until the request is fulfilled.
In rare cases, it is possible that request cannot be fulfilled, and consumer does not receive requested data. To refund deposited $KAIA in such cases, one must first cancel request by calling cancelRequest
inside of RequestResponseCoordinator
and then withdraw $KAIA (withdrawTemporary
) from temporary account inside of Prepayment
contract. In both cases, consumer smart contract has to be the sender (msg.sender
). Our consumer smart contract therefore has to include such auxiliary function(s) to make appropriate calls. If we do not add such functions to consumer contract, it will not be possible to cancel request and withdraw funds deposited to temporary account. Deposited funds will be then forever locked inside of Prepayment
contract.
The code listing below is an example of function inside of consumer contract to cancel and withdraw funds from temporary account.
Request data with Temporary Account (coordinator)
The following function is defined in RequestResponseCoordinator
contract.
This function first calculates a fee (fee
) for the request by calling estimateDirectPaymentFee()
function. isDirectPayment
variable indicates whether the request is created through Prepayment or Direct Payment method. Then, it deposits the required fee (fee
) to the account by calling s_prepayment.deposit(accId)
and passing the fee (fee
) as value. If the amount of $KAIA passed by msg.value
to the requestData
is larger than required fee (fee
), the remaining amount is sent back to the caller using the msg.sender.call()
method. Finally, the function returns requestId
that is generated by the requestDataInternal()
function.
This function first calculates a fee for the request by calling estimateFee()
function. Then, it create a temporary account inside of Prepayment contract with sPrepayment.createTemporaryAccount(msg.sender)
call. In the next step, we request data by calling requestData
function. The function has several validation steps, therefore we included requesting for data before depositing the required fee to the account (sPrepayment.depositTemporary{value: fee}(accId)
). If the amount of $KAIA passed by msg.value
to the requestData
is larger than a required fee, the remaining amount is sent back to the refundRecipient
address. Finally, the function returns requestId
that is generated by the internal requestData()
call.
Request & Response Post-Processing
The Orakl Network Request-Response solution enables consumers to define their own requests on-chain, process them by off-chain oracle, and report the results back to consumer smart contract on chain.
Requests are created with the help of the Orakl library. Every request is associated with a job ID which describes what data type it expects to report. The list of currently supported report data types can be found in the table below.
uint128
keccak256(abi.encodePacked("uint128")
int256
keccak256(abi.encodePacked("int256")
boolean
keccak256(abi.encodePacked("boolean")
bytes32
keccak256(abi.encodePacked("bytes32")
bytes
keccak256(abi.encodePacked("bytes")
string
keccak256(abi.encodePacked("string")
Request
The job identifier is used to initialize the Orakl.Request
data structure. Once the request is received by the off-chain oracle, it knows what data type to use for final reporting.
Instance of the Orakl.Request
holds all information about consumer's API request and how to post-process the API response. Both request, and post-processing details are inserted to the instance of Orakl.Request
using add
function. The add
function accepts key-value pair parameters, where the first one represents the type of data passed, and the second one is the data itself. The first inserted key has to be get
, and the value has to be a valid API URL.
If the first key is not
get
with valid API URL link as a value, then the request will fail and will not be processed.
Response Post-Processing
The Orakl Network Request-Response currently supports five different post-processing operations that are listed in the table below.
path
list of keys for walk through input JSON
req.add("RAW,ETH,USD,PRICE")
index
Array index
req.add("index", "2");
mul
Multiplication
req.add("mul", "2");
div
Division
req.add("div", "2");
pow10
Multiplication by
req.add("pow10", "8");
Last updated