# Calling an Airnode
A requester is a contract that can trigger an Airnode request. To do so, the requester needs to be sponsored and make the request using a matching sponsor wallet. See Requesters and Sponsors on how to sponsor a requester and derive the sponsor wallet.
Airnode consists of two parts: the off-chain Airnode (a.k.a. "the node") deployed as self hosted or cloud provider functions, e.g., AWS) and the on-chain protocol contract AirnodeRrp.sol. A requester calls the protocol contract, which emits a blockchain event with the request parameters. Airnode listens to the events emitted by the AirnodeRrp contract. During the next run cycle, Airnode gets the request parameters from the emitted event. The diagram below and the diagram in the Overview doc for developers illustrate the mechanics of the entire process.
The AirnodeRrp protocol is designed to be flexible and is meant to serve a variety of use cases. See the Airnode requester examples (opens new window) for potential design patterns.
Ignoring the mechanics of the overall process, the requester calling an Airnode primarily focuses on two tasks, indicated by points A & B in the diagram below.
- 1: Make the request
- 2: Accept and decode the response
A requester makes a request to the AirnodeRrp contract which adds the
requestId
to storage, emits the request to the event logs and returns therequestId
to the requester. The request is retrieved by the Airnode during its next run cycle. It then verifies the requester is authorized by checking authorizer contracts assigned to the Airnode.If the request is authorized, Airnode proceeds to respond. It first gathers the requested data from the API and calls the
fulfill()
function in AirnodeRrp, which removes the pendingrequestId
from storage and makes a callback tomyFulfill()
. The gas costs associated with the response are covered by the sponsor of the requester.
The following section of this document discusses the requester implementation, its deployment and sponsoring.
# Step #1: Inherit RrpRequester.sol
A requester inherits from the RrpRequester.sol (opens new window) contract. This will expose the AirnodeRrp.sol protocol contract to the requester allowing it to make Airnode requests.
import "@api3/airnode-protocol/contracts/rrp/requesters/RrpRequester.sol";
contract MyRequester is RrpRequester {
...
constructor (address airnodeRrpAddress)
public
RrpRequester(airnodeRrpAddress)
{}
...
}
2
3
4
5
6
7
8
9
10
Note the constructor parameter airnodeRrpAddress
, which is the public address
of the AirnodeRrp.sol protocol contract on the blockchain you wish to use. It is
used by RrpRequester.sol to point itself to AirnodeRrp.sol.
See the list of all Airnode contract addresses in the reference section.
# Step #2: Implement the request logic
There are two types of requests provided by the AirnodeRrp.sol contract. See the Request page for information related to each request type.
This example uses a full request type
(note the airnodeRrp.makeFullRequest
function call in the code below) which is
called from the requester's own function callTheAirnode
. The function
makeFullRequest
requires that the requester pass all parameters needed by
Airnode to call its underlying API.
Once the request has been made to airnodeRrp.makeFullRequest
, the
AirnodeRrp.sol contract returns a requestId
confirming the request has been
accepted and is in process of being executed. Your requester would most likely
wish to track all requestId
s. Note the line
incomingFulfillments[requestId] = true;
in the code below that stores the
requestId
s in a mapping. This is useful when the Airnode responds to the
requester later at the function (airnodeCallback
) with the requestId
and the
data
requested.
import "@api3/airnode-protocol/contracts/rrp/requesters/RrpRequester.sol";
contract MyRequester is RrpRequester {
mapping(bytes32 => bool) public incomingFulfillments;
mapping(bytes32 => int256) public fulfilledData;
constructor (address airnodeRrpAddress)
public
RrpRequester(airnodeRrpAddress)
{}
function callTheAirnode(
address airnode,
bytes32 endpointId,
address sponsor,
address sponsorWallet,
bytes calldata parameters // Inbound API parameters which may already be ABI encoded
)
external
{
bytes32 requestId = airnodeRrp.makeFullRequest( // Make the Airnode request
airnode, // airnode
endpointId, // endpointId
sponsor, // sponsor's address
sponsorWallet, // sponsorWallet
address(this), // fulfillAddress
this.airnodeCallback.selector, // fulfillFunctionId
parameters // API parameters
);
incomingFulfillments[requestId] = true;
}
function airnodeCallback( // The AirnodeRrp.sol protocol contract will callback here.
bytes32 requestId,
bytes calldata data
{
...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Request Parameters
A full request using the AirnodeRrp.sol contract makeFullRequest
function
requires all parameters needed by the Airnode application to be passed at
runtime. This is in contrast to a template request that would use a template for
some or all of the required parameters. Learn more about
using templates.
Since the callTheAirnode
function makes a
full request, it must gather the
following parameters to pass on to airnodeRrp.makeFullRequest
.
airnode and endpointId: As a pair, these uniquely identify the endpoint desired at a particular Airnode.
sponsor: The sponsor address.
sponsorWallet: The sponsor wallet address that the sponsor received when deriving the wallet for the Airnode being called.
fulfillAddress and fulfillFunctionId: The public address of your requester contract and its function that is called upon the return of the request.
parameters: Specify the API parameters and any reserved parameters, these must be encoded. See Airnode ABI specifications for how these are encoded.
In most, cases the parameters are encoded off-chain and passed to the requester which only forwards them. You can use the @api3/airnode-abi package to perform the encoding and decoding. Take a look at the JavaScript snippet below.
// JavaScript snippet import { encode } from '@api3/airnode-abi'; const parameters = [ { type: 'string32', name: 'coin', value: 'ETH' }, { type: 'string32', name: 'apiKey', value: 'UHHS7SRGC975E' }, ]; const encodedData = encode(parameters); console.log(encodedData); // '0x...'
1
2
3
4
5
6
7
8
9
10
11
12However, this is not a hard requirement and you can encode the parameters on-chain as well. Take a look at the Solidity snippet below.
// Solidity snippet abi.encode( string32("1SS"), string32("period"), "30d", string32("symbols"), "btc,eth,matic,link,uni,sushi,aave,chz,theta,rsr,grt,enj,ocean,kacy" )
1
2
3
4
5
6
7
For additional information on request parameters when calling
airnodeRrp.makeFullRequest()
, see
Request Parameters in the Reference
section.
# Step #3: Capture the Response
As soon as the Airnode gets a request, it gathers the data, encodes it and
starts an on-chain transaction responding to the request. The Airnode calls the
AirnodeRrp.sol contract function fulfill()
, which in turn calls the requester,
in this case, at airnodeCallback
. For the purposes of the callback, recall the
request supplied the request contract address and the desired callback function
which the AirnodeRrp.sol protocol contract stored with the requestId
.
import "@api3/airnode-protocol/contracts/rrp/requesters/RrpRequester.sol";
contract MyRequester is RrpRequester {
mapping(bytes32 => bool) public incomingFulfillments;
mapping(bytes32 => int256) public fulfilledData;
constructor (address airnodeRrpAddress)
public
RrpRequester(airnodeRrpAddress)
{}
function callTheAirnode(
...
}
function airnodeCallback( // The AirnodeRrp.sol protocol contract will callback here.
bytes32 requestId,
bytes calldata data
)
external
onlyAirnodeRrp
{
require(incomingFulfillments[requestId], "No such request made");
delete incomingFulfillments[requestId];
int256 decodedData = abi.decode(data, (int256));
fulfilledData[requestId] = decodedData;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Response Parameters
The callback to a requester contains two parameters, as shown in the
airnodeCallback
function in the code sample above.
- requestId: First acquired when making the request and passed here as a reference to identify the request for which the response is intended.
- data: In case of a successful response, this is the requested data which
has been encoded and contains a
timestamp
in addition to other response data. Decode it using the function
decode()
from theabi
object.
# Step #4: Deploy and Sponsor the Requester
Deploy the requester to the desired blockchain and then sponsor the requester. See Requesters and Sponsors to learn more about sponsoring a requester.