oracle_soroban_kit/lib.rs
1/*
2 Copyright (c) 2023-2024 Frederic Kyung-jin Rezeau (오경진 吳景振)
3
4 This file is part of soroban-kit.
5
6 Licensed under the MIT License, this software is provided "AS IS",
7 no liability assumed. For details, see the LICENSE file in the
8 root directory.
9
10 Author: Fred Kyung-jin Rezeau <fred@litemint.com>
11*/
12
13// This example implements a simple Oracle Broker to handle synchronous and asynchronous
14// topic-based requests for a fee.
15
16#![no_std]
17
18use soroban_kit::{oracle, oracle_broker};
19use soroban_sdk::{
20 contract, contractimpl, contracttype, token, Address, Bytes, Env, TryIntoVal, Vec,
21};
22
23#[contracttype]
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct Message {
26 pub data: Bytes,
27 pub timestamp: u64,
28}
29
30#[contracttype]
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub enum Database {
33 Topic(Bytes),
34}
35
36#[contract]
37// In this example, we use `Bytes` (native type) for the topic and a custom type
38// `Message` for the data. All built-in and user defined types are supported.
39#[oracle_broker(Bytes, Message)]
40pub struct OracleContract;
41
42// Implement the Oracle events trait.
43impl oracle::Events<Bytes, Message> for OracleContract {
44 // This event is fired for each data requests.
45 fn on_subscribe(env: &Env, topic: &Bytes, envelope: &oracle::Envelope) -> Option<Message> {
46 // Retrieve the envelopes for this topic.
47 let mut envelopes = if env.storage().instance().has::<Bytes>(topic) {
48 env.storage()
49 .instance()
50 .get::<Bytes, Vec<oracle::Envelope>>(topic)
51 .unwrap()
52 } else {
53 Vec::new(env)
54 };
55
56 // Here, you typically handle authentication,
57 // apply filters for routers, subscribers, topics,
58 // other checks as needed.
59
60 // Example: topic-based filter.
61 // assert_eq!(*topic, bytes![env, [1, 2, 3]]);
62
63 // Example: Enforce max envelopes per topics.
64 // assert!(envelopes.len() < 5);
65
66 // In this example, we demonstrate how to charge a fee
67 // for all subscriber requests.
68 envelope.subscriber.require_auth();
69 token::Client::new(
70 &env,
71 &Address::from_string(
72 &"CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
73 .try_into_val(env)
74 .unwrap(),
75 ),
76 )
77 .transfer(
78 &envelope.subscriber,
79 &env.current_contract_address(),
80 &10000000,
81 );
82
83 // Return the data synchronously if available.
84 if env
85 .storage()
86 .instance()
87 .has(&Database::Topic(topic.clone()))
88 {
89 env.storage()
90 .instance()
91 .get::<_, Message>(&Database::Topic(topic.clone()))
92 } else {
93 // Otherwise, add the envelope for asynchronous publishing.
94 envelopes.push_back(envelope.clone());
95 env.storage()
96 .instance()
97 .set::<Bytes, Vec<oracle::Envelope>>(topic, &envelopes);
98 None
99 }
100 }
101
102 // This event is fired for each publishing requests.
103 fn on_publish(
104 env: &Env,
105 topic: &Bytes,
106 data: &Message,
107 _publisher: &Address,
108 ) -> Vec<oracle::Envelope> {
109 // Here, you typically handle authentication for publishers
110 // other checks as needed.
111
112 // Store the data for synchronous requests.
113 env.storage()
114 .instance()
115 .set::<_, Message>(&Database::Topic(topic.clone()), data);
116
117 // In this example, we simply return all envelopes @topic
118 let envelopes = env
119 .storage()
120 .instance()
121 .get::<Bytes, Vec<oracle::Envelope>>(topic)
122 .unwrap();
123 env.storage().instance().remove::<Bytes>(topic);
124 envelopes
125 }
126}
127
128// That's it! Ready to deploy your Oracle broker contract!