Oracles
Introduction
This guide shows you how to perform all the operations that you need within the lifecycle of oracles using the SDK.
1. Oracle: register
You register an oracle that responds with the temperature of the city that is included in the query.
To register an oracle you need to provide a queryFormat
and a responseFormat
to the registerOracle
function of the SDK. In addition to the common transaction options you can provide the oracle specific options queryFee
and oracleTtlValue
, see transaction options.
// init an instance of the SDK using the AeSdk class
const aeSdk = new AeSdk({ ... })
// set TTL with a delta of 1000 blocks
const oracleTtlOptions = { oracleTtlType: ORACLE_TTL_TYPES.delta, oracleTtlValue: 1000 }
// OR set a specific block height to expire
const oracleTtlOptions = { oracleTtlType: ORACLE_TTL_TYPES.block, oracleTtlValue: 555555 }
// queryFee is optional and defaults to 30000
// oracleTtlValue is optional and defaults to 500
// oracleTtlType is optional and defaults to ORACLE_TTL_TYPES.delta
const options = { queryFee: 1337, ...oracleTtlOptions }
// the first argument is the queryFormat and the second is the responseFormat
const oracle = await aeSdk.registerOracle("{'city': string}", "{'temperature': int}", options)
Note:
- By default the oracle will exist for the next 500 KeyBlocks.
- If you intend to keep your oracle running longer you should increase the
oracleTtlValue
and/or set up a service that automatically extends the TTL before it expires. - The
oracleId
will be similar to the address of the account that registered the Oracle. - The only difference is the prefix that will be
ok_
instead ofak_
- This means that each account can only host 1 oracle. It's not possible to manage multiple oracles using the same account.
2. Some party: query an oracle and poll for response
Query
After the oracle has been registered and as long as it isn't expired, everybody that knows the oracleId
can query it.
const oracleId = 'ok_...';
const options = {
queryFee: 1337, // should cover the requested fee of the oracle and defaults to 30000
queryTtlType: ORACLE_TTL_TYPES.delta, // optional and defaults to ORACLE_TTL_TYPES.delta
queryTtlValue: 20, // optional and defaults to 10
responseTtlType: ORACLE_TTL_TYPES.delta, // optional and defaults to ORACLE_TTL_TYPES.delta
responseTtlValue: 50, // optional and defaults to 10
};
// using the oracle object in case you need to instantiate the oracle object first
const oracle = await aeSdk.getOracleObject(oracleId)
const query = await oracle.postQuery("{'city': 'Berlin'}", options)
// OR using the aeSdk (instance of AeSdk class) directly by providing the oracleId
const query = await aeSdk.postQueryToOracle(oracleId, "{'city': 'Berlin'}", options)
Note:
- Again, take a look into the transaction options to see what (other) options you can provide.
Poll for response
Now you have access to the query object and can poll for the response to that specific query:
const oracleId = 'ok_...';
const queryId = 'oq_...';
// using the query instance
const query = await aeSdk.getQueryObject(oracleId, queryId) // in case you need to get the query instance first
const response = await query.pollForResponse({ interval: 6000 })
// OR using the aeSdk (instance of AeSdk class) directly by providing the oracleId
const response = await aeSdk.pollForQueryResponse(oracleId, queryId, { interval: 6000 })
// decode the oracle response
// the decode function returns a buffer that needs to be converted to a string
const decodedResponse = String(response.decode());
console.log(decodedResponse)
3. Oracle: poll for queries and respond
Poll for queries
Typically, the oracle itself polls for its own queries and responds as soon as possible:
const stopPolling = await oracle.pollQueries((query) => {
console.log(query) // log a new query
}, { interval: 1000 }) // polling interval in milliseconds
stopPolling() // stop polling
Note:
- Probably the oracle would respond here directly (see below) instead of just logging the queries.
Respond to query
If the oracle recognizes that it has been queried it can respond to the query as long as the query's TTL has not been expired.
const oracleId = 'ok_...';
const queryId = 'oq_...';
const options = { onAccount: 'ak_...' } // only the account of the oracle can respond to the query
// using the query instance
const query = await aeSdk.getQueryObject(oracleId, queryId)
await query.respond('{ "temperature": 27.5 }', options)
// OR using the aeSdk (instance of AeSdk class) directly by providing the queryId
await aeSdk.respondToQuery(queryId, '{ "temperature": 27.5 }', options)
Note:
- Of course the oracle itself would either use an API to get the current temperature for a certain city or ideally directly communicate with measuring devices located in that specific city.
- If the AeSdk class is initialized with the oracle's account there is no need to pass the
onAccount
option as this is done implicitely.
4. Oracle: extend
As mentioned above an Oracle has a certain TTL that can be specified when registering it. You might want to extend the TTL of the oracle before it expires. You can do that as follows:
const oracleId = 'ok_...';
// extend TTL by additional 500 blocks (based on current expiration height of the oracle)
const options = { oracleTtlType: ORACLE_TTL_TYPES.delta, oracleTtlValue: 500 }
// using the oracle instance
const oracle = await aeSdk.getOracleObject(oracleId)
const extendedOracle = await oracle.extendOracle(options)
// OR using the aeSdk (instance of AeSdk class) directly
const extendedOracle = await aeSdk.extendOracleTtl(options)
Example applications
- ae-oracle-pricefeed
- NodeJS example that registers an oracle, extends it if required and responds to queries automatically.