From ff8f20a1b7f2a27b53698c151e8405c809076cb6 Mon Sep 17 00:00:00 2001 From: xlassix Date: Thu, 16 Jan 2025 01:05:51 +0100 Subject: [PATCH 1/3] Add Verity Move Oracles dependencies and example module --- examples/oracke/Move.toml | 20 +++ examples/oracke/Readme.md | 0 examples/oracke/sources/example.move | 246 +++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 examples/oracke/Move.toml create mode 100644 examples/oracke/Readme.md create mode 100644 examples/oracke/sources/example.move diff --git a/examples/oracke/Move.toml b/examples/oracke/Move.toml new file mode 100644 index 0000000000..f66b6e8455 --- /dev/null +++ b/examples/oracke/Move.toml @@ -0,0 +1,20 @@ +[package] +name = "verity-move-example" +version = "0.0.1" + +[dependencies] +MoveStdlib = { git = "https://github.com/rooch-network/rooch.git", subdir = "frameworks/move-stdlib", rev = "main" } +MoveosStdlib = { git = "https://github.com/rooch-network/rooch.git", subdir = "frameworks/moveos-stdlib", rev = "main" } +RoochFramework = { git = "https://github.com/rooch-network/rooch.git", subdir = "frameworks/rooch-framework", rev = "main" } +VerityMoveOracles = { git = "https://github.com/usherlabs/verity-move-oracles.git", subdir = "rooch", rev = "feature/open-ai" } # change feature/open-ai to main once PR#15 is merged + + +[addresses] +verity = "0x0d6144b074dd19a9ff581abd5bf7815a39222c8b3ac68ce5938c9d9723544e08" +verity_oracle_example = "_" +std = "0x1" +moveos_std = "0x2" +rooch_framework = "0x3" + +[dev-addresses] +verity_oracle_example = "0x43" \ No newline at end of file diff --git a/examples/oracke/Readme.md b/examples/oracke/Readme.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/oracke/sources/example.move b/examples/oracke/sources/example.move new file mode 100644 index 0000000000..cf6e27fd16 --- /dev/null +++ b/examples/oracke/sources/example.move @@ -0,0 +1,246 @@ +// Copyright (c) Usher Labs +// SPDX-License-Identifier: LGPL-2.1 + +// ? This module is an example caller used to demonstrate how to deploy Contracts on Rooch that integrate with Verity Move Oracles. +// ? Please keep aware of the OPTIONAL section in this module. +module verity_oracle_example::example_caller { + use moveos_std::event; + use moveos_std::account; + use moveos_std::object::{ObjectID}; + use std::option::{Self, Option}; + use std::vector; + use std::string::String; + use verity::oracles::{Self as Oracles}; + use rooch_framework::gas_coin::RGas; + use rooch_framework::account_coin_store; + #[test_only] + use verity::oracles; + + struct GlobalParams has key { + pending_requests: vector, + } + + #[test_only] + public fun init_for_test(){ + oracles::init_for_test(); + init(); + } + + + // ? ------ OPTIONAL ------ + // ? This is totally OPTIONAL + struct RequestFulfilledEvent has copy, drop { + request_url: String, + request_method: String, + response: Option, + } + // \ ------ OPTIONAL ------ + + // Initiate the module with an empty vector of pending requests + // Requests are managed in the caller to prevent other modules from impersonating the calling module, and spoofing new data. + fun init(){ + // let params = account::borrow_mut_resource(@verity_test_foreign_module); // account::borrow_mut_resource in init throws an error on deployment + // params.pending_requests = vector::empty(); + let signer = moveos_std::signer::module_signer(); + account::move_resource_to(&signer, GlobalParams { pending_requests: vector::empty() }); + } + + public entry fun request_data( + from: &signer, + url: String, + method: String, + headers: String, + body: String, + pick: String, + oracle: address, + amount: u256 + ) { + let http_request = Oracles::build_request(url, method, headers, body); + + // We're passing the address and function identifier of the recipient address. in this from :: + // If you do not want to pay for the Oracle to notify your contract, you can pass in option::none() as the argument. + let payment = account_coin_store::withdraw(from, amount); + let request_id = Oracles::new_request_with_payment(http_request, pick, oracle, Oracles::with_notify(@verity_test_foreign_module, b"example_caller::receive_data"),payment); + // let no_notify_request_id = Oracles::new_request(http_request, pick, oracle, Oracles::without_notify()); + let params = account::borrow_mut_resource(@verity_test_foreign_module); + vector::push_back(&mut params.pending_requests, request_id); + } + + // This notify function is called by the Oracle. + // ! It must not include parameters, or return arguments. + public entry fun receive_data() { + let params = account::borrow_mut_resource(@verity_test_foreign_module); + let pending_requests = params.pending_requests; + + let i = 0; + while (i < vector::length(&pending_requests)) { + let request_id = vector::borrow(&pending_requests, i); + // Remove the fulfilled request from the pending_requests vector + // This ensures unfulfilled requests are retained in the vector + if (option::is_some(&Oracles::get_response(request_id))) { + vector::remove(&mut params.pending_requests, i); + // Decrement i to account for the removed element + if (i > 0) { + i = i - 1; + }; + + // ? ------ OPTIONAL ------ + let request_url = Oracles::get_request_params_url(request_id); + let request_method = Oracles::get_request_params_method(request_id); + let response = Oracles::get_response(request_id); + // For each fulfilment, emit an event + event::emit(RequestFulfilledEvent { + request_url, + request_method, + response, + }); + // \ ------ OPTIONAL ------ + }; + + i = i + 1; + }; + } + + #[view] + public fun pending_requests_count(): u64 { + let params = account::borrow_resource(@verity_test_foreign_module); + vector::length(¶ms.pending_requests) + } +} + +#[test_only] +module verity_oracle_example::test_foreign_module { + use moveos_std::signer; + use verity_test_foreign_module::example_caller::{Self, request_data, pending_requests_count}; + use rooch_framework::gas_coin; + use verity::registry; + use std::vector; + + + + #[test_only] + struct Test has key {} + + #[test_only] + struct TestOrchestrator has key {} + + #[test_only] + fun setup_test() { + example_caller::init_for_test(); + } + + #[test] + fun test_request_data_basic() { + setup_test(); + let test_signer = moveos_std::signer::module_signer(); + let test_orchestrator = moveos_std::signer::module_signer(); + + + + // Setup test parameters + let url = std::string::utf8(b"https://api.test.com"); + let method = std::string::utf8(b"GET"); + let headers = std::string::utf8(b"Content-Type: application/json"); + let body = std::string::utf8(b""); + let pick = std::string::utf8(b"$.data"); + let amount = 100000u256; + + registry::add_supported_url(&test_orchestrator, url, 100, 0, 1, 0); + + + // Fund the test account + gas_coin::faucet_entry(&test_signer, amount); + + // Make request + request_data(&test_signer, url, method, headers, body, pick, signer::address_of(&test_orchestrator), amount); + + // Verify request was stored + + assert!(pending_requests_count() == 1, 0); + } + + + #[test] + fun test_multiple_requests() { + setup_test(); + let test_signer = moveos_std::signer::module_signer(); + let test_orchestrator = moveos_std::signer::module_signer(); + + + // Setup test parameters for multiple requests + let urls = vector[ + std::string::utf8(b"https://api.test.com/2/test/id2"), + std::string::utf8(b"https://api.test.com/2/my_profile"), + std::string::utf8(b"https://api.test.com/2/test") + ]; + let method = std::string::utf8(b"GET"); + let headers = std::string::utf8(b"Content-Type: application/json"); + let body = std::string::utf8(b""); + let pick = std::string::utf8(b"$.data"); + let amount = 100u256; + + registry::add_supported_url(&test_orchestrator, std::string::utf8(b"https://api.test.com/2/"), 100, 0, 1, 0); + + + // Fund the test account with enough for multiple requests + let total_amount = amount * 4; + gas_coin::faucet_entry(&test_signer, total_amount); + + // Make multiple requests + let i = 0; + while (i < vector::length(&urls)) { + let url = *vector::borrow(&urls, i); + request_data(&test_signer, url, method, headers, body, pick, signer::address_of(&test_orchestrator), amount); + i = i + 1; + }; + + // Verify all requests were stored + assert!(pending_requests_count() == 3, 2); + + } + + #[test] + #[expected_failure(abort_code = rooch_framework::coin_store::ErrorInsufficientBalance, location = rooch_framework::coin_store)] // Adjust abort code as needed + fun test_insufficient_funds() { + setup_test(); + let test_signer = moveos_std::signer::module_signer(); + + let url = std::string::utf8(b"https://api.test.com"); + let method = std::string::utf8(b"GET"); + let headers = std::string::utf8(b"Content-Type: application/json"); + let body = std::string::utf8(b""); + let pick = std::string::utf8(b"$.data"); + let oracle = @0x1; + let amount = 100u256; + + + + gas_coin::faucet_entry(&test_signer, amount/10); + request_data(&test_signer, url, method, headers, body, pick, oracle, amount); + } + + #[test] + fun test_request_with_body() { + setup_test(); + let test_signer = moveos_std::signer::module_signer(); + let test_orchestrator = moveos_std::signer::module_signer(); + + let url = std::string::utf8(b"https://api.test.com/yud"); + let method = std::string::utf8(b"POST"); + let headers = std::string::utf8(b"Content-Type: application/json"); + let body = std::string::utf8(b"{\"key\":\"value\"}"); + let pick = std::string::utf8(b".data"); + let oracle = signer::address_of(&test_orchestrator); + let amount = 1000u256; + + + gas_coin::faucet_entry(&test_signer, amount); + + + registry::add_supported_url(&test_orchestrator, std::string::utf8(b"https://api.test.com/"), 100, 0, 1, 0); + request_data(&test_signer, url, method, headers, body, pick, oracle, amount); + + assert!(pending_requests_count() == 1, 4); + + } +} From da643343568ed6cfdf9fec5e23bb920b07877f48 Mon Sep 17 00:00:00 2001 From: xlassix Date: Thu, 16 Jan 2025 01:07:25 +0100 Subject: [PATCH 2/3] Add TODO comment to Readme.md --- examples/oracke/Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/oracke/Readme.md b/examples/oracke/Readme.md index e69de29bb2..0ffdd02fcb 100644 --- a/examples/oracke/Readme.md +++ b/examples/oracke/Readme.md @@ -0,0 +1 @@ +// TODO \ No newline at end of file From 39f025238768f46c7b0c0f2ac46ea21c30c2b04d Mon Sep 17 00:00:00 2001 From: xlassix Date: Thu, 16 Jan 2025 01:19:17 +0100 Subject: [PATCH 3/3] Add Rooch Oracle example with detailed documentation --- examples/oracke/Readme.md | 70 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/examples/oracke/Readme.md b/examples/oracke/Readme.md index 0ffdd02fcb..6783da4de1 100644 --- a/examples/oracke/Readme.md +++ b/examples/oracke/Readme.md @@ -1 +1,69 @@ -// TODO \ No newline at end of file +# Rooch Oracle Example + +This example demonstrates how to use and interact with Oracles in the Rooch Network. + +## Documentation + +For detailed documentation on setting up and using Oracles in Rooch Network, including: + +- Prerequisites +- Step-by-step setup instructions +- Account creation +- Contract deployment +- Environment configuration +- URL management +- Running the orchestrator +- Making oracle requests +- Managing escrow balance + +Please refer to our comprehensive guide in [docs/ROOCH.md](https://github.com/usherlabs/verity-move-oracles/blob/main/docs/ROOCH.md). + +## Quick Start + +1. Follow the setup instructions in [docs/ROOCH.md](https://github.com/usherlabs/verity-move-oracles/blob/main/docs/ROOCH.md) +2. Deploy the oracle contracts +3. Configure your environment +4. Run the orchestrator +5. Make oracle requests + +## Supported APIs + +### Twitter/X API +- User Endpoint: `https://api.x.com/2/users/` and `https://api.x.com/2/tweets/` + ```bash + # Example: Get user followers count + rooch move run --function ::example_caller::request_data \ + --sender-account default \ + --args 'string:https://api.x.com/2/users/by/username/elonmusk?user.fields=public_metrics' \ + --args 'string:GET' \ + --args 'string:{}' \ + --args 'string:{}' \ + --args 'string:.data.public_metrics.followers_count' \ + --args 'address:' \ + --args 'u256:50000000' + ``` + +### OpenAI API +- Chat Completions: `https://api.openai.com/v1/chat/completions` + ```bash + # Example: Simple GPT request + rooch move run --function ::example_caller::request_data \ + --sender-account default \ + --args 'string:https://api.openai.com/v1/chat/completions' \ + --args 'string:POST' \ + --args 'string:{}' \ + --args 'string:{ + "model": "gpt-4", + "messages": [{"role": "user", "content": "Say this is a test!"}], + "temperature": 0.7 + }' \ + --args 'string:.choices[].message.content' \ + --args 'address:' \ + --args 'u256:50000000' + ``` + +Note: Replace `` and `` with your actual deployed addresses. + +## Support + +For additional help or questions, please refer to the main documentation or open an issue in the [repository](https://github.com/usherlabs/verity-move-oracles.git). \ No newline at end of file