Oracle Example Guide
The Oracle hook allows you to manage and update data programmatically using a combination of binary models and C hooks. Here's a quickstart guide to get you up and running with the Oracle hook.
Create the Binary Models
Binary models are used to define the structure of the data that you want to manage with your Oracle. You'll need to create models that represent the data you're interested in.
Oracle Model
This model represents an individual oracle entry with an issuer, currency, and value.
import {
BaseModel,
Metadata,
Currency,
XRPAddress,
XFL,
} from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models'
export class OracleModel extends BaseModel {
issuer: XRPAddress
currency: Currency
value: XFL
// 48 bytes
constructor(
issuer: XRPAddress, // 20 byte / 0
currency: Currency, // 20 byte / 20
value: XFL // 8 byte / 40
) {
super()
this.issuer = issuer
this.currency = currency
this.value = value
}
getMetadata(): Metadata {
return [
{ field: 'issuer', type: 'xrpAddress' },
{ field: 'currency', type: 'currency' },
{ field: 'value', type: 'xfl' },
]
}
toJSON() {
return {
issuer: this.issuer,
currency: this.currency,
value: this.value,
}
}
}
Oracle Array Model
This model represents an array of OracleModel entries.
import {
BaseModel,
Metadata,
} from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models'
import { OracleModel } from './OracleModel'
export class OracleArrayModel extends BaseModel {
oracles: OracleModel[]
constructor(oracles: OracleModel[]) {
super()
this.oracles = oracles
}
getMetadata(): Metadata {
return [
{
field: 'oracles',
type: 'varModelArray',
modelClass: OracleModel,
maxArrayLength: 100,
},
]
}
toJSON() {
return {
oracles: this.oracles,
}
}
}
Create the C Hook
The C hook is responsible for processing the data sent to the Oracle and updating the state accordingly.
//------------------------------------------------------------------------------
/*
Copyright (c) 2024 Transia, LLC
This financial product is intended for use by individuals or entities who
possess the necessary licenses and qualifications to solicit and utilize
such products in accordance with applicable laws and regulations.
Unauthorized use or distribution of this product may be subject to legal
consequences.
The information provided in this financial product is for informational
purposes only and should not be considered as financial advice or a
recommendation to engage in any specific investment or financial strategy.
It is important to consult with a qualified professional or financial
advisor before making any investment decisions.
*/
//==============================================================================
#include "hookapi.h"
int64_t hook(uint32_t reserved)
{
TRACESTR("oracle.c: Called.");
// ACCOUNT: Hook Account
uint8_t hook_accid[20];
hook_account(hook_accid, 20);
// ACCOUNT: OTXN Account
uint8_t otxn_accid[20];
otxn_field(otxn_accid, 20, sfAccount);
if (!BUFFER_EQUAL_20(hook_accid, otxn_accid))
{
rollback(SBUF("oracle.c: Invalid OTXN `Account`"), __LINE__);
}
uint8_t txn_id[32];
ASSERT(otxn_id(txn_id, 32, 0) == 32);
ASSERT(otxn_slot(1) == 1);
ASSERT(slot_subfield(1, sfBlob, 2) == 2);
uint8_t buffer[676];
ASSERT(slot(SBUF(buffer), 2) > 0);
uint16_t len = (uint16_t)buffer[0];
uint8_t* ptr = buffer + 1;
if (len > 192)
{
len = 193 + ((len - 193) * 256) + ((uint16_t)(buffer[1]));
ptr++;
}
uint8_t* end = ptr + len;
while (ptr < end)
{
GUARD(48);
uint8_t hash[32];
util_sha512h(SBUF(hash), ptr, 40);
ASSERT(state_set(ptr + 40, 8, hash, 32) == 8);
ptr += 48;
}
accept(SBUF("oracle.c: Updated."), __LINE__);
_g(1, 1);
return 0;
}
Create the main() function file
The main()
function file is where you'll use the models you've created to encode data and send it to the Oracle.
// xrpl
import { Invoke, SetHookFlags, TransactionMetadata } from '@transia/xrpl'
// xrpl-helpers
import {
XrplIntegrationTestContext,
setupClient,
teardownClient,
serverUrl,
} from '@transia/hooks-toolkit/dist/npm/src/libs/xrpl-helpers'
// src
import {
Xrpld,
SetHookParams,
setHooksV3,
createHookPayload,
ExecutionUtility,
generateHash,
StateUtility,
hexNamespace,
clearHookStateV3,
clearAllHooksV3,
iHook,
} from '@transia/hooks-toolkit'
import { OracleModel } from './models/OracleModel'
import { OracleArrayModel } from './models/OracleArrayModel'
import { hexToXfl } from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models'
async function main() {
const testContext: XrplIntegrationTestContext = await setupClient(serverUrl)
const hookWallet = testContext.hook1
const acct1hook1 = createHookPayload({
version: 0,
createFile: 'oracle',
namespace: 'oracle',
flags: SetHookFlags.hsfOverride,
hookOnArray: ['Invoke'],
})
await setHooksV3({
client: testContext.client,
seed: hookWallet.seed,
hooks: [{ Hook: acct1hook1 }],
} as SetHookParams)
// Invoke - Update the oracle
const oracleModel: OracleModel = new OracleModel(
testContext.ic.issuer,
testContext.ic.currency,
1.2
)
const oracleArray: OracleArrayModel = new OracleArrayModel([oracleModel])
const builtTx1: Invoke = {
TransactionType: 'Invoke',
Account: hookWallet.classicAddress,
Blob: oracleArray.encode().slice(2, oracleArray.encode().length),
}
const result1 = await Xrpld.submit(testContext.client, {
wallet: hookWallet,
tx: builtTx1,
})
const hookExecutions1 = await ExecutionUtility.getHookExecutionsFromMeta(
testContext.client,
result1.meta as TransactionMetadata
)
const ns = generateHash(
Buffer.from(
oracleArray.encode().slice(2, oracleArray.encode().length - 16),
'hex'
)
)
const state = await StateUtility.getHookState(
testContext.client,
hookWallet.classicAddress,
ns,
hexNamespace('oracle')
)
console.log(state)
}
main()
Build the hook and run the main function
Finally, you'll need to compile the C hook and deploy it to the Xahau network. Then, run the main()
function to invoke the Oracle with your data.
- Compile the C hook using the
c2wasm-cli
. - Execute the
main()
function to deploy the hook and update the Oracle.