1 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
/*
Date: 2023
Author: Fred Kyung-jin Rezeau <fred@litemint.com>
Copyright (c) 2023 Litemint LLC
MIT License
*/
// This example implements a simple Oracle Broker to handle synchronous and asynchronous
// topic-based requests for a fee.
#![no_std]
use soroban_kit::{oracle, oracle_broker};
use soroban_sdk::{
contract, contractimpl, contracttype, token, Address, Bytes, Env, TryIntoVal, Vec,
};
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Message {
pub data: Bytes,
pub timestamp: u64,
}
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Database {
Topic(Bytes),
}
#[contract]
// In this example, we use `Bytes` (native type) for the topic and a custom type
// `Message` for the data. All built-in and user defined types are supported.
#[oracle_broker(Bytes, Message)]
pub struct OracleContract;
// Implement the Oracle events trait.
impl oracle::Events<Bytes, Message> for OracleContract {
// This event is fired for each data requests.
fn on_subscribe(env: &Env, topic: &Bytes, envelope: &oracle::Envelope) -> Option<Message> {
// Retrieve the envelopes for this topic.
let mut envelopes = if env.storage().instance().has::<Bytes>(topic) {
env.storage()
.instance()
.get::<Bytes, Vec<oracle::Envelope>>(topic)
.unwrap()
} else {
Vec::new(env)
};
// Here, you typically handle authentication,
// apply filters for routers, subscribers, topics,
// other checks as needed.
// Example: topic-based filter.
// assert_eq!(*topic, bytes![env, [1, 2, 3]]);
// Example: Enforce max envelopes per topics.
// assert!(envelopes.len() < 5);
// In this example, we demonstrate how to charge a fee
// for all subscriber requests.
envelope.subscriber.require_auth();
token::Client::new(
&env,
&Address::from_string(
&"CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
.try_into_val(env)
.unwrap(),
),
)
.transfer(
&envelope.subscriber,
&env.current_contract_address(),
&10000000,
);
// Return the data synchronously if available.
if env
.storage()
.instance()
.has(&Database::Topic(topic.clone()))
{
env.storage()
.instance()
.get::<_, Message>(&Database::Topic(topic.clone()))
} else {
// Otherwise, add the envelope for asynchronous publishing.
envelopes.push_back(envelope.clone());
env.storage()
.instance()
.set::<Bytes, Vec<oracle::Envelope>>(topic, &envelopes);
None
}
}
// This event is fired for each publishing requests.
fn on_publish(
env: &Env,
topic: &Bytes,
data: &Message,
_publisher: &Address,
) -> Vec<oracle::Envelope> {
// Here, you typically handle authentication for publishers
// other checks as needed.
// Store the data for synchronous requests.
env.storage()
.instance()
.set::<_, Message>(&Database::Topic(topic.clone()), data);
// In this example, we simply return all envelopes @topic
let envelopes = env
.storage()
.instance()
.get::<Bytes, Vec<oracle::Envelope>>(topic)
.unwrap();
env.storage().instance().remove::<Bytes>(topic);
envelopes
}
}
// That's it! Ready to deploy your Oracle broker contract!