Beyond pre-alpha RRP
After a year of on-field testing, we are introducing a batch of updates to Airnode RRP (request–response protocol), which will greatly improve on the pre-alpha version. This article is for the ones who are already familiar with pre-alpha, and just want to know what has changed and why.
Terminology
We want Airnode to be the default tool to integrate an API to a smart contract, and we need strong developer adoption for this. The first step of adopting a tool is understanding it, and a minimal and clear terminology is needed to facilitate this. The pre-alpha version has accumulated some debt in this regard, which is dealt with in this update by renaming the concepts below.
Client -> Requester
In pre-alpha, the contract (or less likely, the EOA) that makes the request (i.e., calls makeRequest()) is called the client. This was a point of confusion because:
- How come the entity that makes the request is not called the requester, yet another is (see below)?
- The client can specify a different address to be called back in their request. Then, the name becomes semantically questionable. (Who is the client, the one who makes the request, or the one who gets called back with the response?)
The solution is simple: Call the contract that makes the request a requester.
Requester -> Sponsor
In pre-alpha, the entity that pays for the request fulfillment gas costs of a client was called a requester. Again, two problems:
- The requester did not actually make the request. Their role was solely financing the client’s requests.
- The term requester is now used to refer to clients (see above), so we no longer can use it.
As a result, we went with a name that is much more descriptive of what a requester does, which is sponsor.
Designated wallet -> Sponsor wallet
A designated wallet in pre-alpha is a wallet that an Airnode has designated for a requester for them to fund client requests. This refers to a pre-pre-alpha version of the protocol where the Airnode had to actively designate these wallets by making a transaction for each. The current name is both misleading and bloats our lexicon. Instead, we went with the simple sponsor wallet, which obviously refers to the wallet of the respective sponsor.
Endorsing -> Sponsoring
In pre-alpha, a requester endorses a client, which means allowing it to use their designated wallet. Similar to “designated wallet”, this is just more made up jargon, which may annoy people. Instead, we now simply say that a sponsor sponsors a requester, which means allowing it to use their sponsor wallet.
Regular request -> Template request
In pre-alpha, a regular request (or request for short) is a request where the client refers to a request template for parameters. This is now called a template request for clarity.
Functional updates
We also reduced the friction in the way our protocol works, mostly by reducing the number of transactions that both sides need to make.
Airnodes are identified by the address of their default BIP 44 wallet
In pre-alpha, the airnodeId that identifies an Airnode is the hash of the address of its master wallet (derived with the path m). Now, we use the address of the default BIP 44 wallet (derived with the path m/44'/60'/0'/0/0), called the Airnode address. This means that when you plug in the mnemonic you use to deploy your Airnode, the address that will pop up on your Metamask will be your Airnode address.
Airnode extended public key belongs to a new path
In pre-alpha, the Airnode operator needed to announce the extended public key (xpub) derived from the HD node with the path m. Now, the announced path needs to belong to the HD node with the path m/44'/60'/0'. This means that if you use the xpub of an Airnode to derive the address with the path 0/0, you will get the Airnode address.
Airnode address is the Airnode admin
When a contract requires to seek permission from an Airnode to whitelist requesters, to allow an account to whitelist requesters, etc., it requires the caller to be the Airnode address, i.e., there is no longer a separate Airnode admin address to be specified. This greatly streamlined the deployment process and the user flow for future Airnode management products such as ChainAPI and Airnode Management UI .
The Airnode provides the authorizer contract addresses while making authorizer checks
In pre-alpha, Airnodes set their authorizer contract addresses on-chain, and the authorization checks were made against the contracts at these addresses. However, this didn’t provide any guarantees, as the Airnode doesn’t have to abide by the authorization result (i.e., can fulfill unauthorized requests and vice versa). Instead, we now require the Airnode to provide the authorizer addresses while making the checkAuthorizationStatuses() call, which helps with the next point.
Airnodes don’t create on-chain records
The pre-alpha user flow starts by the Airnode “master wallet” calling setAirnodeParameters() to set its admin address, and announce its extended public key and authorizer contract addresses on-chain. This caused needless integration friction. For example, who funds the Airnode master wallet of an API3 partner? What if the API3 partner really doesn’t want to make any transactions, no matter who funds it?
In addition to the removal of the admin address and the authorizer contract address announcements above, we also no longer require the extended public keys to be announced on-chain (which makes sense because they can’t be verified on-chain, similar to the authorization case above), resulting in Airnodes no longer needing to create any on-chain records for operation.
Fulfillments get signed by the Airnode wallet
In addition to the requester specifying the address they want their request to be fulfilled by, we also require the Airnode wallet to sign the request ID-response pair. This is because even though the former method was secure in that the requester could be sure that the one responding to them is a wallet controlled by the respective Airnode (because they derived its address from the Airnode’s xpub), they couldn’t prove it to other contracts on-chain, which was limiting. With the addition of this requirement, the protocol will now be able to be used in a wider variety of scenarios. This update is completely transparent to requesters and node operators.
Replaced status codes with error strings
We no longer return HTTP-like status codes, but an error string instead. These error strings will allow developers to debug issues easily using tools like RRP Explorer. In addition, we stopped fulfilling errored requests (could have been thought of as an error callback) because we have observed that error handling for oracle requests is a premature concept and the developers don’t seem to know what to do with it, resulting in this potentially causing more harm than good.
All sponsors have a designated sponsor wallet for each Airnode by default
Each sponsor is identified by their address, and their sponsor wallets are designated implicitly by the following path:
0…
/1st least significant 31-bits of the sponsor address…
/2nd least significant 31-bits of the sponsor address…
/3rd least significant 31-bits of the sponsor address…
/4th least significant 31-bits of the sponsor address…
/5th least significant 31-bits of the sponsor address…
/6th least significant 31-bits of the sponsor address
In other words, a sponsor can calculate the address of their respective sponsor wallet for an Airnode (using its xpub) and have requesters use it to make requests right away, without any preliminary transactions (meanwhile pre-alpha required a transaction to get assigned a “requester index”).
Sponsor address is the sponsor admin
Requesters in pre-alpha were identified by indices, and needed to have admin addresses associated to authorize interactions (i.e., only the requester admin could endorse clients). Sponsors are identified by their addresses, so we can use this address directly as the admin. Note that this is the analog of the update that removed the Airnode admin and used its identifying address as the admin.
Designated wallet address removed from authorizer arguments
In pre-alpha, one could implement authorizers that check for requestId, airnodeId, endpointId, requesterIndex, designatedWallet and clientAddress. With the update, authorizers can check for requestId, airnode, endpointId, sponsor, requester. Note that most of these are the renames discussed under Terminology above, yet the notable difference is that we no longer have the designated (sponsor) wallet address.
Withdrawal destinations are not specifiable
From now on, when a sponsor requests a withdrawal, the Airnode will deposit the funds to the sponsor address (instead of an arbitrary address specified in the withdrawal request). This implies that if you are taking your Airnode down, you can simply deposit all the sponsor wallet balances to the sponsor addresses.
Implemented adminnable and authorizer contracts
This is probably by far the most significant update. The pre-alpha protocol left authorizers as a stub, and only allowed the endpoint to be accessible by the public. We implemented two high-level authorizers, DaoRequesterRrpAuthorizer (to be controlled by the API3 DAO and the addresses authorized by it) and AirnodeRequesterRrpAuthorizer (to be controlled by the Airnode addresses and the addresses authorized by them). Furthermore, the lower-level admin functionality is implemented as a chain of inheritance, which can easily be reused to build contracts that need similar whitelist/blacklist functionality (e.g., dAPIs).
Implemented RrpBeaconServer.sol
A beacon is a singular live data point on-chain. A beacon server is a requester contract that houses beacons indexable by the request template ID. It can also be thought of as a highly scalable single-Airnode data feed library that you can both use individually or build dAPIs out of. We are planning to use beacons with the publish–subscribe protocol (PSP) heavily, yet this contract is a preliminary version built on RRP. The admin functionality of this contract is implemented by reusing the admin contracts built for the authorizers.
Conclusion
This article may have overwhelmed you if you’re not already very familiar with pre-alpha, but not to worry! Finalized RRP is much easier to understand in a standalone way, compared to migrating from pre-alpha. For a commentary that doesn’t refer to pre-alpha, see the package README, examples, and wait for the updated docs at docs.api3.org (The last two links will die once they are migrated to the docs.)