From 9a6fd93ca45bcd17aba5557fe87b43e41684af4b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 22:30:17 +0000 Subject: [PATCH 01/12] feat: add Rust client for Hermes Co-Authored-By: Tejas Badadare --- .../workflows/publish-rust-hermes-client.yml | 18 + .../client/rust/.openapi-generator-ignore | 23 + .../client/rust/.openapi-generator/FILES | 46 + .../client/rust/.openapi-generator/VERSION | 1 + apps/hermes/client/rust/Cargo.lock | 1534 +++++++++++++++++ apps/hermes/client/rust/Cargo.toml | 21 + apps/hermes/client/rust/README.md | 108 ++ apps/hermes/client/rust/docs/AssetType.md | 15 + apps/hermes/client/rust/docs/BinaryUpdate.md | 10 + apps/hermes/client/rust/docs/EncodingType.md | 11 + .../client/rust/docs/GetVaaCcipResponse.md | 9 + .../hermes/client/rust/docs/GetVaaResponse.md | 10 + ...estPublisherStakeCapsUpdateDataResponse.md | 10 + .../client/rust/docs/ParsedPriceFeedTwap.md | 13 + .../client/rust/docs/ParsedPriceUpdate.md | 12 + .../rust/docs/ParsedPublisherStakeCap.md | 10 + .../docs/ParsedPublisherStakeCapsUpdate.md | 9 + .../client/rust/docs/PriceFeedMetadata.md | 10 + apps/hermes/client/rust/docs/PriceUpdate.md | 10 + apps/hermes/client/rust/docs/RestApi.md | 400 +++++ apps/hermes/client/rust/docs/RpcPrice.md | 12 + apps/hermes/client/rust/docs/RpcPriceFeed.md | 13 + .../client/rust/docs/RpcPriceFeedMetadata.md | 12 + .../rust/docs/RpcPriceFeedMetadataV2.md | 11 + apps/hermes/client/rust/docs/TwapsResponse.md | 10 + .../client/rust/src/apis/configuration.rs | 51 + apps/hermes/client/rust/src/apis/mod.rs | 116 ++ apps/hermes/client/rust/src/apis/rest_api.rs | 668 +++++++ apps/hermes/client/rust/src/lib.rs | 11 + .../client/rust/src/models/asset_type.rs | 50 + .../client/rust/src/models/binary_update.rs | 30 + .../client/rust/src/models/encoding_type.rs | 38 + .../rust/src/models/get_vaa_ccip_response.rs | 27 + .../rust/src/models/get_vaa_response.rs | 31 + ...blisher_stake_caps_update_data_response.rs | 30 + apps/hermes/client/rust/src/models/mod.rs | 34 + .../rust/src/models/parsed_price_feed_twap.rs | 42 + .../rust/src/models/parsed_price_update.rs | 36 + .../src/models/parsed_publisher_stake_cap.rs | 30 + .../parsed_publisher_stake_caps_update.rs | 27 + .../rust/src/models/price_feed_metadata.rs | 30 + .../client/rust/src/models/price_update.rs | 30 + .../client/rust/src/models/rpc_price.rs | 42 + .../client/rust/src/models/rpc_price_feed.rs | 40 + .../src/models/rpc_price_feed_metadata.rs | 36 + .../src/models/rpc_price_feed_metadata_v2.rs | 33 + .../client/rust/src/models/twaps_response.rs | 31 + 47 files changed, 3801 insertions(+) create mode 100644 .github/workflows/publish-rust-hermes-client.yml create mode 100644 apps/hermes/client/rust/.openapi-generator-ignore create mode 100644 apps/hermes/client/rust/.openapi-generator/FILES create mode 100644 apps/hermes/client/rust/.openapi-generator/VERSION create mode 100644 apps/hermes/client/rust/Cargo.lock create mode 100644 apps/hermes/client/rust/Cargo.toml create mode 100644 apps/hermes/client/rust/README.md create mode 100644 apps/hermes/client/rust/docs/AssetType.md create mode 100644 apps/hermes/client/rust/docs/BinaryUpdate.md create mode 100644 apps/hermes/client/rust/docs/EncodingType.md create mode 100644 apps/hermes/client/rust/docs/GetVaaCcipResponse.md create mode 100644 apps/hermes/client/rust/docs/GetVaaResponse.md create mode 100644 apps/hermes/client/rust/docs/LatestPublisherStakeCapsUpdateDataResponse.md create mode 100644 apps/hermes/client/rust/docs/ParsedPriceFeedTwap.md create mode 100644 apps/hermes/client/rust/docs/ParsedPriceUpdate.md create mode 100644 apps/hermes/client/rust/docs/ParsedPublisherStakeCap.md create mode 100644 apps/hermes/client/rust/docs/ParsedPublisherStakeCapsUpdate.md create mode 100644 apps/hermes/client/rust/docs/PriceFeedMetadata.md create mode 100644 apps/hermes/client/rust/docs/PriceUpdate.md create mode 100644 apps/hermes/client/rust/docs/RestApi.md create mode 100644 apps/hermes/client/rust/docs/RpcPrice.md create mode 100644 apps/hermes/client/rust/docs/RpcPriceFeed.md create mode 100644 apps/hermes/client/rust/docs/RpcPriceFeedMetadata.md create mode 100644 apps/hermes/client/rust/docs/RpcPriceFeedMetadataV2.md create mode 100644 apps/hermes/client/rust/docs/TwapsResponse.md create mode 100644 apps/hermes/client/rust/src/apis/configuration.rs create mode 100644 apps/hermes/client/rust/src/apis/mod.rs create mode 100644 apps/hermes/client/rust/src/apis/rest_api.rs create mode 100644 apps/hermes/client/rust/src/lib.rs create mode 100644 apps/hermes/client/rust/src/models/asset_type.rs create mode 100644 apps/hermes/client/rust/src/models/binary_update.rs create mode 100644 apps/hermes/client/rust/src/models/encoding_type.rs create mode 100644 apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs create mode 100644 apps/hermes/client/rust/src/models/get_vaa_response.rs create mode 100644 apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs create mode 100644 apps/hermes/client/rust/src/models/mod.rs create mode 100644 apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs create mode 100644 apps/hermes/client/rust/src/models/parsed_price_update.rs create mode 100644 apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs create mode 100644 apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs create mode 100644 apps/hermes/client/rust/src/models/price_feed_metadata.rs create mode 100644 apps/hermes/client/rust/src/models/price_update.rs create mode 100644 apps/hermes/client/rust/src/models/rpc_price.rs create mode 100644 apps/hermes/client/rust/src/models/rpc_price_feed.rs create mode 100644 apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs create mode 100644 apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs create mode 100644 apps/hermes/client/rust/src/models/twaps_response.rs diff --git a/.github/workflows/publish-rust-hermes-client.yml b/.github/workflows/publish-rust-hermes-client.yml new file mode 100644 index 0000000000..06f38d0e80 --- /dev/null +++ b/.github/workflows/publish-rust-hermes-client.yml @@ -0,0 +1,18 @@ +name: Publish Rust package hermes-client to crates.io + +on: + push: + tags: + - hermes-client-rust-v* +jobs: + publish-hermes-client: + name: Publish Rust package hermes-client to crates.io + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - run: cargo publish --token ${CARGO_REGISTRY_TOKEN} + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + working-directory: "apps/hermes/client/rust" diff --git a/apps/hermes/client/rust/.openapi-generator-ignore b/apps/hermes/client/rust/.openapi-generator-ignore new file mode 100644 index 0000000000..7484ee590a --- /dev/null +++ b/apps/hermes/client/rust/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/apps/hermes/client/rust/.openapi-generator/FILES b/apps/hermes/client/rust/.openapi-generator/FILES new file mode 100644 index 0000000000..7453028b61 --- /dev/null +++ b/apps/hermes/client/rust/.openapi-generator/FILES @@ -0,0 +1,46 @@ +.gitignore +.openapi-generator-ignore +.travis.yml +Cargo.toml +README.md +docs/AssetType.md +docs/BinaryUpdate.md +docs/EncodingType.md +docs/GetVaaCcipResponse.md +docs/GetVaaResponse.md +docs/LatestPublisherStakeCapsUpdateDataResponse.md +docs/ParsedPriceFeedTwap.md +docs/ParsedPriceUpdate.md +docs/ParsedPublisherStakeCap.md +docs/ParsedPublisherStakeCapsUpdate.md +docs/PriceFeedMetadata.md +docs/PriceUpdate.md +docs/RestApi.md +docs/RpcPrice.md +docs/RpcPriceFeed.md +docs/RpcPriceFeedMetadata.md +docs/RpcPriceFeedMetadataV2.md +docs/TwapsResponse.md +git_push.sh +src/apis/configuration.rs +src/apis/mod.rs +src/apis/rest_api.rs +src/lib.rs +src/models/asset_type.rs +src/models/binary_update.rs +src/models/encoding_type.rs +src/models/get_vaa_ccip_response.rs +src/models/get_vaa_response.rs +src/models/latest_publisher_stake_caps_update_data_response.rs +src/models/mod.rs +src/models/parsed_price_feed_twap.rs +src/models/parsed_price_update.rs +src/models/parsed_publisher_stake_cap.rs +src/models/parsed_publisher_stake_caps_update.rs +src/models/price_feed_metadata.rs +src/models/price_update.rs +src/models/rpc_price.rs +src/models/rpc_price_feed.rs +src/models/rpc_price_feed_metadata.rs +src/models/rpc_price_feed_metadata_v2.rs +src/models/twaps_response.rs diff --git a/apps/hermes/client/rust/.openapi-generator/VERSION b/apps/hermes/client/rust/.openapi-generator/VERSION new file mode 100644 index 0000000000..eb1dc6a51a --- /dev/null +++ b/apps/hermes/client/rust/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.13.0 diff --git a/apps/hermes/client/rust/Cargo.lock b/apps/hermes/client/rust/Cargo.lock new file mode 100644 index 0000000000..cc676f482d --- /dev/null +++ b/apps/hermes/client/rust/Cargo.lock @@ -0,0 +1,1534 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "hermes-client" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "derive_more", + "futures-util", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serde_with", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.3", + "serde", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + +[[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/apps/hermes/client/rust/Cargo.toml b/apps/hermes/client/rust/Cargo.toml new file mode 100644 index 0000000000..ea9c8a2b14 --- /dev/null +++ b/apps/hermes/client/rust/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "hermes-client" +version = "0.1.0" +authors = ["Pyth Network Contributors"] +description = "A Rust client for Pyth Hermes price service" +license = "Apache-2.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_with = { version = "3.8", default-features = false, features = ["base64", "std", "macros"] } +serde_json = "1.0" +serde_repr = "0.1" +url = "2.5" +reqwest = { version = "0.12", default-features = false, features = ["json", "multipart"] } +tokio = { version = "1.0", features = ["full"] } +anyhow = "1.0" +futures-util = "0.3" +derive_more = { version = "1.0.0", features = ["from"] } +base64 = "0.22.1" +tracing = "0.1" diff --git a/apps/hermes/client/rust/README.md b/apps/hermes/client/rust/README.md new file mode 100644 index 0000000000..1e5f1991b9 --- /dev/null +++ b/apps/hermes/client/rust/README.md @@ -0,0 +1,108 @@ +# Hermes Rust Client + +A Rust client for interacting with the Pyth Network's real-time pricing data through the Hermes service. + +## Description + +This package provides a Rust client for the Hermes service, which serves Pyth price feeds. It allows you to easily fetch price feeds and subscribe to real-time price updates. + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +hermes-client = "0.1.0" +``` + +## Usage + +```rust +use hermes_client::apis::rest_api; +use hermes_client::apis::configuration::Configuration; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create a new configuration with the Hermes endpoint + let mut config = Configuration::new(); + config.base_path = "https://hermes.pyth.network".to_string(); + + // Fetch latest price updates for a specific price feed + let price_feed_id = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"; + let price_updates = rest_api::latest_price_updates(&config, Some(&[price_feed_id]), None, None).await?; + println!("Price updates: {:?}", price_updates); + + Ok(()) +} +``` + +## API Documentation + +For detailed API documentation, you can generate the documentation locally: + +```bash +cargo doc --open +``` + +The client provides access to all Hermes API endpoints, including: + +- Fetching latest price updates +- Getting price feed metadata +- Retrieving TWAPs (Time-Weighted Average Prices) +- Accessing publisher stake caps +- Streaming real-time price updates + +## Code Generation + +This client was generated from the [Hermes OpenAPI specification](https://hermes.pyth.network/docs/openapi.json) using the OpenAPI Generator tool. + +### Regeneration Steps + +To regenerate the client after updates to the Hermes API: + +1. Install OpenAPI Generator CLI: + ```bash + npm install @openapitools/openapi-generator-cli -g + ``` + +2. Generate the client: + ```bash + npx @openapitools/openapi-generator-cli generate \ + -i https://hermes.pyth.network/docs/openapi.json \ + -g rust \ + -o apps/hermes/client/rust \ + --additional-properties=packageName=hermes-client,packageVersion=0.1.0 + ``` + +3. Manually update dependencies in Cargo.toml and version number if necessary. + +## Publishing + +This package is published to crates.io when a new tag matching `hermes-client-rust-v*` is pushed. + +## Hermes Public Endpoint + +The client defaults to using the Hermes public endpoint at https://hermes.pyth.network. For production applications, we recommend using a dedicated endpoint for better reliability. + +## Documentation For Models + + - [AssetType](docs/AssetType.md) + - [BinaryUpdate](docs/BinaryUpdate.md) + - [EncodingType](docs/EncodingType.md) + - [GetVaaCcipResponse](docs/GetVaaCcipResponse.md) + - [GetVaaResponse](docs/GetVaaResponse.md) + - [LatestPublisherStakeCapsUpdateDataResponse](docs/LatestPublisherStakeCapsUpdateDataResponse.md) + - [ParsedPriceFeedTwap](docs/ParsedPriceFeedTwap.md) + - [ParsedPriceUpdate](docs/ParsedPriceUpdate.md) + - [ParsedPublisherStakeCap](docs/ParsedPublisherStakeCap.md) + - [ParsedPublisherStakeCapsUpdate](docs/ParsedPublisherStakeCapsUpdate.md) + - [PriceFeedMetadata](docs/PriceFeedMetadata.md) + - [PriceUpdate](docs/PriceUpdate.md) + - [RpcPrice](docs/RpcPrice.md) + - [RpcPriceFeed](docs/RpcPriceFeed.md) + - [RpcPriceFeedMetadata](docs/RpcPriceFeedMetadata.md) + - [RpcPriceFeedMetadataV2](docs/RpcPriceFeedMetadataV2.md) + - [TwapsResponse](docs/TwapsResponse.md) + + + diff --git a/apps/hermes/client/rust/docs/AssetType.md b/apps/hermes/client/rust/docs/AssetType.md new file mode 100644 index 0000000000..c1abcd38d4 --- /dev/null +++ b/apps/hermes/client/rust/docs/AssetType.md @@ -0,0 +1,15 @@ +# AssetType + +## Enum Variants + +| Name | Value | +|---- | -----| +| Crypto | crypto | +| Fx | fx | +| Equity | equity | +| Metal | metal | +| Rates | rates | +| CryptoRedemptionRate | crypto_redemption_rate | + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/BinaryUpdate.md b/apps/hermes/client/rust/docs/BinaryUpdate.md new file mode 100644 index 0000000000..38de3a500f --- /dev/null +++ b/apps/hermes/client/rust/docs/BinaryUpdate.md @@ -0,0 +1,10 @@ +# BinaryUpdate + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**data** | **Vec** | | +**encoding** | [**models::EncodingType**](EncodingType.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/EncodingType.md b/apps/hermes/client/rust/docs/EncodingType.md new file mode 100644 index 0000000000..6fb7dee9dd --- /dev/null +++ b/apps/hermes/client/rust/docs/EncodingType.md @@ -0,0 +1,11 @@ +# EncodingType + +## Enum Variants + +| Name | Value | +|---- | -----| +| Hex | hex | +| Base64 | base64 | + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/GetVaaCcipResponse.md b/apps/hermes/client/rust/docs/GetVaaCcipResponse.md new file mode 100644 index 0000000000..4e803a6ded --- /dev/null +++ b/apps/hermes/client/rust/docs/GetVaaCcipResponse.md @@ -0,0 +1,9 @@ +# GetVaaCcipResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**data** | **String** | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/GetVaaResponse.md b/apps/hermes/client/rust/docs/GetVaaResponse.md new file mode 100644 index 0000000000..f03e3858c0 --- /dev/null +++ b/apps/hermes/client/rust/docs/GetVaaResponse.md @@ -0,0 +1,10 @@ +# GetVaaResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**publish_time** | **i64** | | +**vaa** | **String** | The VAA binary represented as a base64 string. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/LatestPublisherStakeCapsUpdateDataResponse.md b/apps/hermes/client/rust/docs/LatestPublisherStakeCapsUpdateDataResponse.md new file mode 100644 index 0000000000..74430ed2ff --- /dev/null +++ b/apps/hermes/client/rust/docs/LatestPublisherStakeCapsUpdateDataResponse.md @@ -0,0 +1,10 @@ +# LatestPublisherStakeCapsUpdateDataResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**binary** | [**models::BinaryUpdate**](BinaryUpdate.md) | | +**parsed** | Option<[**Vec**](ParsedPublisherStakeCapsUpdate.md)> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/ParsedPriceFeedTwap.md b/apps/hermes/client/rust/docs/ParsedPriceFeedTwap.md new file mode 100644 index 0000000000..c5c193f777 --- /dev/null +++ b/apps/hermes/client/rust/docs/ParsedPriceFeedTwap.md @@ -0,0 +1,13 @@ +# ParsedPriceFeedTwap + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**down_slots_ratio** | **String** | The % of slots where the network was down over the TWAP window. A value of zero indicates no slots were missed over the window, and a value of one indicates that every slot was missed over the window. This is a float value stored as a string to avoid precision loss. | +**end_timestamp** | **i64** | The end unix timestamp of the window | +**id** | **String** | | +**start_timestamp** | **i64** | The start unix timestamp of the window | +**twap** | [**models::RpcPrice**](RpcPrice.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/ParsedPriceUpdate.md b/apps/hermes/client/rust/docs/ParsedPriceUpdate.md new file mode 100644 index 0000000000..aa87b6a473 --- /dev/null +++ b/apps/hermes/client/rust/docs/ParsedPriceUpdate.md @@ -0,0 +1,12 @@ +# ParsedPriceUpdate + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ema_price** | [**models::RpcPrice**](RpcPrice.md) | | +**id** | **String** | | +**metadata** | [**models::RpcPriceFeedMetadataV2**](RpcPriceFeedMetadataV2.md) | | +**price** | [**models::RpcPrice**](RpcPrice.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/ParsedPublisherStakeCap.md b/apps/hermes/client/rust/docs/ParsedPublisherStakeCap.md new file mode 100644 index 0000000000..4c845d4a4e --- /dev/null +++ b/apps/hermes/client/rust/docs/ParsedPublisherStakeCap.md @@ -0,0 +1,10 @@ +# ParsedPublisherStakeCap + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**cap** | **i64** | | +**publisher** | **String** | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/ParsedPublisherStakeCapsUpdate.md b/apps/hermes/client/rust/docs/ParsedPublisherStakeCapsUpdate.md new file mode 100644 index 0000000000..7f97193cf4 --- /dev/null +++ b/apps/hermes/client/rust/docs/ParsedPublisherStakeCapsUpdate.md @@ -0,0 +1,9 @@ +# ParsedPublisherStakeCapsUpdate + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**publisher_stake_caps** | [**Vec**](ParsedPublisherStakeCap.md) | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/PriceFeedMetadata.md b/apps/hermes/client/rust/docs/PriceFeedMetadata.md new file mode 100644 index 0000000000..4e68af1f1d --- /dev/null +++ b/apps/hermes/client/rust/docs/PriceFeedMetadata.md @@ -0,0 +1,10 @@ +# PriceFeedMetadata + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**attributes** | **std::collections::HashMap** | | +**id** | **String** | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/PriceUpdate.md b/apps/hermes/client/rust/docs/PriceUpdate.md new file mode 100644 index 0000000000..1162701b80 --- /dev/null +++ b/apps/hermes/client/rust/docs/PriceUpdate.md @@ -0,0 +1,10 @@ +# PriceUpdate + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**binary** | [**models::BinaryUpdate**](BinaryUpdate.md) | | +**parsed** | Option<[**Vec**](ParsedPriceUpdate.md)> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/RestApi.md b/apps/hermes/client/rust/docs/RestApi.md new file mode 100644 index 0000000000..8915a8552a --- /dev/null +++ b/apps/hermes/client/rust/docs/RestApi.md @@ -0,0 +1,400 @@ +# \RestApi + +All URIs are relative to *http://localhost* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**get_price_feed**](RestApi.md#get_price_feed) | **GET** /api/get_price_feed | **Deprecated: use /v2/updates/price/{publish_time} instead** +[**get_vaa**](RestApi.md#get_vaa) | **GET** /api/get_vaa | **Deprecated: use /v2/updates/price/{publish_time} instead** +[**get_vaa_ccip**](RestApi.md#get_vaa_ccip) | **GET** /api/get_vaa_ccip | **Deprecated: use /v2/updates/price/{publish_time} instead** +[**latest_price_feeds**](RestApi.md#latest_price_feeds) | **GET** /api/latest_price_feeds | **Deprecated: use /v2/updates/price/latest instead** +[**latest_price_updates**](RestApi.md#latest_price_updates) | **GET** /v2/updates/price/latest | Get the latest price updates by price feed id. +[**latest_publisher_stake_caps**](RestApi.md#latest_publisher_stake_caps) | **GET** /v2/updates/publisher_stake_caps/latest | Get the most recent publisher stake caps update data. +[**latest_twaps**](RestApi.md#latest_twaps) | **GET** /v2/updates/twap/{window_seconds}/latest | Get the latest TWAP by price feed id with a custom time window. +[**latest_vaas**](RestApi.md#latest_vaas) | **GET** /api/latest_vaas | **Deprecated: use /v2/updates/price/latest instead** +[**price_feed_ids**](RestApi.md#price_feed_ids) | **GET** /api/price_feed_ids | **Deprecated: use /v2/price_feeds instead** +[**price_feeds_metadata**](RestApi.md#price_feeds_metadata) | **GET** /v2/price_feeds | Get the set of price feeds. +[**price_stream_sse_handler**](RestApi.md#price_stream_sse_handler) | **GET** /v2/updates/price/stream | SSE route handler for streaming price updates. +[**timestamp_price_updates**](RestApi.md#timestamp_price_updates) | **GET** /v2/updates/price/{publish_time} | Get the latest price updates by price feed id. + + + +## get_price_feed + +> models::RpcPriceFeed get_price_feed(id, publish_time, verbose, binary) +**Deprecated: use /v2/updates/price/{publish_time} instead** + +**Deprecated: use /v2/updates/price/{publish_time} instead** Get a price update for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**id** | **String** | The id of the price feed to get an update for. | [required] | +**publish_time** | **i64** | The unix timestamp in seconds. This endpoint will return the first update whose publish_time is >= the provided value. | [required] | +**verbose** | Option<**bool**> | If true, include the `metadata` field in the response with additional metadata about the price update. | | +**binary** | Option<**bool**> | If true, include the binary price update in the `vaa` field of each returned feed. This binary data can be submitted to Pyth contracts to update the on-chain price. | | + +### Return type + +[**models::RpcPriceFeed**](RpcPriceFeed.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## get_vaa + +> models::GetVaaResponse get_vaa(id, publish_time) +**Deprecated: use /v2/updates/price/{publish_time} instead** + +**Deprecated: use /v2/updates/price/{publish_time} instead** Get a VAA for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**id** | **String** | The ID of the price feed to get an update for. | [required] | +**publish_time** | **i64** | The unix timestamp in seconds. This endpoint will return the first update whose publish_time is >= the provided value. | [required] | + +### Return type + +[**models::GetVaaResponse**](GetVaaResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json, text/plain + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## get_vaa_ccip + +> models::GetVaaCcipResponse get_vaa_ccip(data) +**Deprecated: use /v2/updates/price/{publish_time} instead** + +**Deprecated: use /v2/updates/price/{publish_time} instead** Get a VAA for a price feed using CCIP This endpoint accepts a single argument which is a hex-encoded byte string of the following form: ` ` + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**data** | **std::path::PathBuf** | | [required] | + +### Return type + +[**models::GetVaaCcipResponse**](GetVaaCcipResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## latest_price_feeds + +> Vec latest_price_feeds(ids_left_square_bracket_right_square_bracket, verbose, binary) +**Deprecated: use /v2/updates/price/latest instead** + +**Deprecated: use /v2/updates/price/latest instead** Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**ids_left_square_bracket_right_square_bracket** | [**Vec**](String.md) | Get the most recent price update for this set of price feed ids. This parameter can be provided multiple times to retrieve multiple price updates, for example see the following query string: ``` ?ids[]=a12...&ids[]=b4c... ``` | [required] | +**verbose** | Option<**bool**> | If true, include the `metadata` field in the response with additional metadata about the price update. | | +**binary** | Option<**bool**> | If true, include the binary price update in the `vaa` field of each returned feed. This binary data can be submitted to Pyth contracts to update the on-chain price. | | + +### Return type + +[**Vec**](RpcPriceFeed.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## latest_price_updates + +> models::PriceUpdate latest_price_updates(ids_left_square_bracket_right_square_bracket, encoding, parsed, ignore_invalid_price_ids) +Get the latest price updates by price feed id. + +Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**ids_left_square_bracket_right_square_bracket** | [**Vec**](String.md) | Get the most recent price update for this set of price feed ids. This parameter can be provided multiple times to retrieve multiple price updates, for example see the following query string: ``` ?ids[]=a12...&ids[]=b4c... ``` | [required] | +**encoding** | Option<[**EncodingType**](.md)> | Optional encoding type. If true, return the price update in the encoding specified by the encoding parameter. Default is `hex`. | | +**parsed** | Option<**bool**> | If true, include the parsed price update in the `parsed` field of each returned feed. Default is `true`. | | +**ignore_invalid_price_ids** | Option<**bool**> | If true, invalid price IDs in the `ids` parameter are ignored. Only applicable to the v2 APIs. Default is `false`. | | + +### Return type + +[**models::PriceUpdate**](PriceUpdate.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json, text/plain + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## latest_publisher_stake_caps + +> models::LatestPublisherStakeCapsUpdateDataResponse latest_publisher_stake_caps(encoding, parsed) +Get the most recent publisher stake caps update data. + +Get the most recent publisher stake caps update data. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**encoding** | Option<[**EncodingType**](.md)> | Get the most recent publisher stake caps update data. Optional encoding type. If true, return the message in the encoding specified by the encoding parameter. Default is `hex`. | | +**parsed** | Option<**bool**> | If true, include the parsed update in the `parsed` field of each returned feed. Default is `true`. | | + +### Return type + +[**models::LatestPublisherStakeCapsUpdateDataResponse**](LatestPublisherStakeCapsUpdateDataResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## latest_twaps + +> models::TwapsResponse latest_twaps(window_seconds, ids_left_square_bracket_right_square_bracket, encoding, parsed, ignore_invalid_price_ids) +Get the latest TWAP by price feed id with a custom time window. + +Get the latest TWAP by price feed id with a custom time window. Given a collection of price feed ids, retrieve the latest Pyth TWAP price for each price feed. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**window_seconds** | **i64** | The time window in seconds over which to calculate the TWAP, ending at the current time. For example, a value of 300 would return the most recent 5 minute TWAP. Must be greater than 0 and less than or equal to 600 seconds (10 minutes). | [required] | +**ids_left_square_bracket_right_square_bracket** | [**Vec**](String.md) | Get the most recent TWAP (time weighted average price) for this set of price feed ids. The `binary` data contains the signed start & end cumulative price updates needed to calculate the TWAPs on-chain. The `parsed` data contains the calculated TWAPs. This parameter can be provided multiple times to retrieve multiple price updates, for example see the following query string: ``` ?ids[]=a12...&ids[]=b4c... ``` | [required] | +**encoding** | Option<[**EncodingType**](.md)> | Optional encoding type. If true, return the cumulative price updates in the encoding specified by the encoding parameter. Default is `hex`. | | +**parsed** | Option<**bool**> | If true, include the calculated TWAP in the `parsed` field of each returned feed. Default is `true`. | | +**ignore_invalid_price_ids** | Option<**bool**> | If true, invalid price IDs in the `ids` parameter are ignored. Only applicable to the v2 APIs. Default is `false`. | | + +### Return type + +[**models::TwapsResponse**](TwapsResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json, text/plain + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## latest_vaas + +> Vec latest_vaas(ids_left_square_bracket_right_square_bracket) +**Deprecated: use /v2/updates/price/latest instead** + +**Deprecated: use /v2/updates/price/latest instead** Get VAAs for a set of price feed ids. Given a collection of price feed ids, retrieve the latest VAA for each. The returned VAA(s) can be submitted to the Pyth contract to update the on-chain price. If VAAs are not found for every provided price ID the call will fail. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**ids_left_square_bracket_right_square_bracket** | [**Vec**](String.md) | Get the VAAs for this set of price feed ids. This parameter can be provided multiple times to retrieve multiple price updates, for example see the following query string: ``` ?ids[]=a12...&ids[]=b4c... ``` | [required] | + +### Return type + +**Vec** + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## price_feed_ids + +> Vec price_feed_ids() +**Deprecated: use /v2/price_feeds instead** + +**Deprecated: use /v2/price_feeds instead** Get the set of price feed IDs. This endpoint fetches all of the price feed IDs for which price updates can be retrieved. + +### Parameters + +This endpoint does not need any parameter. + +### Return type + +**Vec** + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## price_feeds_metadata + +> Vec price_feeds_metadata(query, asset_type) +Get the set of price feeds. + +Get the set of price feeds. This endpoint fetches all price feeds from the Pyth network. It can be filtered by asset type and query string. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**query** | Option<**String**> | Optional query parameter. If provided, the results will be filtered to all price feeds whose symbol contains the query string. Query string is case insensitive. | | +**asset_type** | Option<[**models::AssetType**](.md)> | Optional query parameter. If provided, the results will be filtered by asset type. Possible values are crypto, equity, fx, metal, rates. Filter string is case insensitive. | | + +### Return type + +[**Vec**](PriceFeedMetadata.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## price_stream_sse_handler + +> models::PriceUpdate price_stream_sse_handler(ids_left_square_bracket_right_square_bracket, encoding, parsed, allow_unordered, benchmarks_only, ignore_invalid_price_ids) +SSE route handler for streaming price updates. + +SSE route handler for streaming price updates. The connection will automatically close after 24 hours to prevent resource leaks. Clients should implement reconnection logic to maintain continuous price updates. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**ids_left_square_bracket_right_square_bracket** | [**Vec**](String.md) | Get the most recent price update for this set of price feed ids. This parameter can be provided multiple times to retrieve multiple price updates, for example see the following query string: ``` ?ids[]=a12...&ids[]=b4c... ``` | [required] | +**encoding** | Option<[**EncodingType**](.md)> | Optional encoding type. If true, return the price update in the encoding specified by the encoding parameter. Default is `hex`. | | +**parsed** | Option<**bool**> | If true, include the parsed price update in the `parsed` field of each returned feed. Default is `true`. | | +**allow_unordered** | Option<**bool**> | If true, allows unordered price updates to be included in the stream. | | +**benchmarks_only** | Option<**bool**> | If true, only include benchmark prices that are the initial price updates at a given timestamp (i.e., prevPubTime != pubTime). | | +**ignore_invalid_price_ids** | Option<**bool**> | If true, invalid price IDs in the `ids` parameter are ignored. Only applicable to the v2 APIs. Default is `false`. | | + +### Return type + +[**models::PriceUpdate**](PriceUpdate.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json, text/plain + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## timestamp_price_updates + +> models::PriceUpdate timestamp_price_updates(publish_time, ids_left_square_bracket_right_square_bracket, encoding, parsed, ignore_invalid_price_ids) +Get the latest price updates by price feed id. + +Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**publish_time** | **i64** | The unix timestamp in seconds. This endpoint will return the first update whose publish_time is >= the provided value. | [required] | +**ids_left_square_bracket_right_square_bracket** | [**Vec**](String.md) | Get the most recent price update for this set of price feed ids. This parameter can be provided multiple times to retrieve multiple price updates, for example see the following query string: ``` ?ids[]=a12...&ids[]=b4c... ``` | [required] | +**encoding** | Option<[**EncodingType**](.md)> | Optional encoding type. If true, return the price update in the encoding specified by the encoding parameter. Default is `hex`. | | +**parsed** | Option<**bool**> | If true, include the parsed price update in the `parsed` field of each returned feed. Default is `true`. | | +**ignore_invalid_price_ids** | Option<**bool**> | If true, invalid price IDs in the `ids` parameter are ignored. Only applicable to the v2 APIs. Default is `false`. | | + +### Return type + +[**models::PriceUpdate**](PriceUpdate.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json, text/plain + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/RpcPrice.md b/apps/hermes/client/rust/docs/RpcPrice.md new file mode 100644 index 0000000000..2f4dd442a5 --- /dev/null +++ b/apps/hermes/client/rust/docs/RpcPrice.md @@ -0,0 +1,12 @@ +# RpcPrice + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**conf** | **String** | The confidence interval associated with the price, stored as a string to avoid precision loss | +**expo** | **i32** | The exponent associated with both the price and confidence interval. Multiply those values by `10^expo` to get the real value. | +**price** | **String** | The price itself, stored as a string to avoid precision loss | +**publish_time** | **i64** | When the price was published. The `publish_time` is a unix timestamp, i.e., the number of seconds since the Unix epoch (00:00:00 UTC on 1 Jan 1970). | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/RpcPriceFeed.md b/apps/hermes/client/rust/docs/RpcPriceFeed.md new file mode 100644 index 0000000000..0f44398efa --- /dev/null +++ b/apps/hermes/client/rust/docs/RpcPriceFeed.md @@ -0,0 +1,13 @@ +# RpcPriceFeed + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**ema_price** | [**models::RpcPrice**](RpcPrice.md) | | +**id** | **String** | | +**metadata** | Option<[**models::RpcPriceFeedMetadata**](RpcPriceFeedMetadata.md)> | | [optional] +**price** | [**models::RpcPrice**](RpcPrice.md) | | +**vaa** | Option<**String**> | The VAA binary represented as a base64 string. | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/RpcPriceFeedMetadata.md b/apps/hermes/client/rust/docs/RpcPriceFeedMetadata.md new file mode 100644 index 0000000000..5f3f15b958 --- /dev/null +++ b/apps/hermes/client/rust/docs/RpcPriceFeedMetadata.md @@ -0,0 +1,12 @@ +# RpcPriceFeedMetadata + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**emitter_chain** | **i32** | | +**prev_publish_time** | Option<**i64**> | | [optional] +**price_service_receive_time** | Option<**i64**> | | [optional] +**slot** | Option<**i64**> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/RpcPriceFeedMetadataV2.md b/apps/hermes/client/rust/docs/RpcPriceFeedMetadataV2.md new file mode 100644 index 0000000000..fd97b3da2c --- /dev/null +++ b/apps/hermes/client/rust/docs/RpcPriceFeedMetadataV2.md @@ -0,0 +1,11 @@ +# RpcPriceFeedMetadataV2 + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**prev_publish_time** | Option<**i64**> | | [optional] +**proof_available_time** | Option<**i64**> | | [optional] +**slot** | Option<**i64**> | | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/docs/TwapsResponse.md b/apps/hermes/client/rust/docs/TwapsResponse.md new file mode 100644 index 0000000000..1bf41ed3bf --- /dev/null +++ b/apps/hermes/client/rust/docs/TwapsResponse.md @@ -0,0 +1,10 @@ +# TwapsResponse + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**binary** | [**models::BinaryUpdate**](BinaryUpdate.md) | | +**parsed** | Option<[**Vec**](ParsedPriceFeedTwap.md)> | The calculated TWAPs for each price ID | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/apps/hermes/client/rust/src/apis/configuration.rs b/apps/hermes/client/rust/src/apis/configuration.rs new file mode 100644 index 0000000000..68aa0f2069 --- /dev/null +++ b/apps/hermes/client/rust/src/apis/configuration.rs @@ -0,0 +1,51 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + + + +#[derive(Debug, Clone)] +pub struct Configuration { + pub base_path: String, + pub user_agent: Option, + pub client: reqwest::Client, + pub basic_auth: Option, + pub oauth_access_token: Option, + pub bearer_access_token: Option, + pub api_key: Option, +} + +pub type BasicAuth = (String, Option); + +#[derive(Debug, Clone)] +pub struct ApiKey { + pub prefix: Option, + pub key: String, +} + + +impl Configuration { + pub fn new() -> Configuration { + Configuration::default() + } +} + +impl Default for Configuration { + fn default() -> Self { + Configuration { + base_path: "http://localhost".to_owned(), + user_agent: Some("OpenAPI-Generator/0.8.6/rust".to_owned()), + client: reqwest::Client::new(), + basic_auth: None, + oauth_access_token: None, + bearer_access_token: None, + api_key: None, + } + } +} diff --git a/apps/hermes/client/rust/src/apis/mod.rs b/apps/hermes/client/rust/src/apis/mod.rs new file mode 100644 index 0000000000..9298e979ca --- /dev/null +++ b/apps/hermes/client/rust/src/apis/mod.rs @@ -0,0 +1,116 @@ +use std::error; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct ResponseContent { + pub status: reqwest::StatusCode, + pub content: String, + pub entity: Option, +} + +#[derive(Debug)] +pub enum Error { + Reqwest(reqwest::Error), + Serde(serde_json::Error), + Io(std::io::Error), + ResponseError(ResponseContent), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (module, e) = match self { + Error::Reqwest(e) => ("reqwest", e.to_string()), + Error::Serde(e) => ("serde", e.to_string()), + Error::Io(e) => ("IO", e.to_string()), + Error::ResponseError(e) => ("response", format!("status code {}", e.status)), + }; + write!(f, "error in {}: {}", module, e) + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match self { + Error::Reqwest(e) => e, + Error::Serde(e) => e, + Error::Io(e) => e, + Error::ResponseError(_) => return None, + }) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::Reqwest(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Serde(e) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +pub fn urlencode>(s: T) -> String { + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() +} + +pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { + if let serde_json::Value::Object(object) = value { + let mut params = vec![]; + + for (key, value) in object { + match value { + serde_json::Value::Object(_) => params.append(&mut parse_deep_object( + &format!("{}[{}]", prefix, key), + value, + )), + serde_json::Value::Array(array) => { + for (i, value) in array.iter().enumerate() { + params.append(&mut parse_deep_object( + &format!("{}[{}][{}]", prefix, key, i), + value, + )); + } + }, + serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), + _ => params.push((format!("{}[{}]", prefix, key), value.to_string())), + } + } + + return params; + } + + unimplemented!("Only objects are supported with style=deepObject") +} + +/// Internal use only +/// A content type supported by this client. +#[allow(dead_code)] +enum ContentType { + Json, + Text, + Unsupported(String) +} + +impl From<&str> for ContentType { + fn from(content_type: &str) -> Self { + if content_type.starts_with("application") && content_type.contains("json") { + return Self::Json; + } else if content_type.starts_with("text/plain") { + return Self::Text; + } else { + return Self::Unsupported(content_type.to_string()); + } + } +} + +pub mod rest_api; + +pub mod configuration; diff --git a/apps/hermes/client/rust/src/apis/rest_api.rs b/apps/hermes/client/rust/src/apis/rest_api.rs new file mode 100644 index 0000000000..803761638b --- /dev/null +++ b/apps/hermes/client/rust/src/apis/rest_api.rs @@ -0,0 +1,668 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + + +use reqwest; +use serde::{Deserialize, Serialize, de::Error as _}; +use crate::{apis::ResponseContent, models}; +use super::{Error, configuration, ContentType}; + + +/// struct for typed errors of method [`get_price_feed`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum GetPriceFeedError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`get_vaa`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum GetVaaError { + Status404(String), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`get_vaa_ccip`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum GetVaaCcipError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`latest_price_feeds`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum LatestPriceFeedsError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`latest_price_updates`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum LatestPriceUpdatesError { + Status404(String), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`latest_publisher_stake_caps`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum LatestPublisherStakeCapsError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`latest_twaps`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum LatestTwapsError { + Status404(String), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`latest_vaas`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum LatestVaasError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`price_feed_ids`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PriceFeedIdsError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`price_feeds_metadata`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PriceFeedsMetadataError { + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`price_stream_sse_handler`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PriceStreamSseHandlerError { + Status404(String), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`timestamp_price_updates`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum TimestampPriceUpdatesError { + Status404(String), + UnknownValue(serde_json::Value), +} + + +/// **Deprecated: use /v2/updates/price/{publish_time} instead** Get a price update for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp. +pub async fn get_price_feed(configuration: &configuration::Configuration, id: &str, publish_time: i64, verbose: Option, binary: Option) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_id = id; + let p_publish_time = publish_time; + let p_verbose = verbose; + let p_binary = binary; + + let uri_str = format!("{}/api/get_price_feed", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = req_builder.query(&[("id", &p_id.to_string())]); + req_builder = req_builder.query(&[("publish_time", &p_publish_time.to_string())]); + if let Some(ref param_value) = p_verbose { + req_builder = req_builder.query(&[("verbose", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_binary { + req_builder = req_builder.query(&[("binary", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RpcPriceFeed`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::RpcPriceFeed`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// **Deprecated: use /v2/updates/price/{publish_time} instead** Get a VAA for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp. +pub async fn get_vaa(configuration: &configuration::Configuration, id: &str, publish_time: i64) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_id = id; + let p_publish_time = publish_time; + + let uri_str = format!("{}/api/get_vaa", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = req_builder.query(&[("id", &p_id.to_string())]); + req_builder = req_builder.query(&[("publish_time", &p_publish_time.to_string())]); + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetVaaResponse`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetVaaResponse`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// **Deprecated: use /v2/updates/price/{publish_time} instead** Get a VAA for a price feed using CCIP This endpoint accepts a single argument which is a hex-encoded byte string of the following form: ` ` +pub async fn get_vaa_ccip(configuration: &configuration::Configuration, data: std::path::PathBuf) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_data = data; + + let uri_str = format!("{}/api/get_vaa_ccip", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = req_builder.query(&[("data", &p_data.display().to_string())]); + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetVaaCcipResponse`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetVaaCcipResponse`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// **Deprecated: use /v2/updates/price/latest instead** Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. +pub async fn latest_price_feeds(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec, verbose: Option, binary: Option) -> Result, Error> { + // add a prefix to parameters to efficiently prevent name collisions + let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_verbose = verbose; + let p_binary = binary; + + let uri_str = format!("{}/api/latest_price_feeds", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = match "multi" { + "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + if let Some(ref param_value) = p_verbose { + req_builder = req_builder.query(&[("verbose", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_binary { + req_builder = req_builder.query(&[("binary", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<models::RpcPriceFeed>`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<models::RpcPriceFeed>`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. +pub async fn latest_price_updates(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_encoding = encoding; + let p_parsed = parsed; + let p_ignore_invalid_price_ids = ignore_invalid_price_ids; + + let uri_str = format!("{}/v2/updates/price/latest", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = match "multi" { + "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + if let Some(ref param_value) = p_encoding { + req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_parsed { + req_builder = req_builder.query(&[("parsed", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_ignore_invalid_price_ids { + req_builder = req_builder.query(&[("ignore_invalid_price_ids", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// Get the most recent publisher stake caps update data. +pub async fn latest_publisher_stake_caps(configuration: &configuration::Configuration, encoding: Option, parsed: Option) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_encoding = encoding; + let p_parsed = parsed; + + let uri_str = format!("{}/v2/updates/publisher_stake_caps/latest", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + if let Some(ref param_value) = p_encoding { + req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_parsed { + req_builder = req_builder.query(&[("parsed", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LatestPublisherStakeCapsUpdateDataResponse`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::LatestPublisherStakeCapsUpdateDataResponse`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// Get the latest TWAP by price feed id with a custom time window. Given a collection of price feed ids, retrieve the latest Pyth TWAP price for each price feed. +pub async fn latest_twaps(configuration: &configuration::Configuration, window_seconds: i64, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_window_seconds = window_seconds; + let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_encoding = encoding; + let p_parsed = parsed; + let p_ignore_invalid_price_ids = ignore_invalid_price_ids; + + let uri_str = format!("{}/v2/updates/twap/{window_seconds}/latest", configuration.base_path, window_seconds=p_window_seconds); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = match "multi" { + "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + if let Some(ref param_value) = p_encoding { + req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_parsed { + req_builder = req_builder.query(&[("parsed", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_ignore_invalid_price_ids { + req_builder = req_builder.query(&[("ignore_invalid_price_ids", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TwapsResponse`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::TwapsResponse`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// **Deprecated: use /v2/updates/price/latest instead** Get VAAs for a set of price feed ids. Given a collection of price feed ids, retrieve the latest VAA for each. The returned VAA(s) can be submitted to the Pyth contract to update the on-chain price. If VAAs are not found for every provided price ID the call will fail. +pub async fn latest_vaas(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec) -> Result, Error> { + // add a prefix to parameters to efficiently prevent name collisions + let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + + let uri_str = format!("{}/api/latest_vaas", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = match "multi" { + "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<String>`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<String>`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// **Deprecated: use /v2/price_feeds instead** Get the set of price feed IDs. This endpoint fetches all of the price feed IDs for which price updates can be retrieved. +pub async fn price_feed_ids(configuration: &configuration::Configuration, ) -> Result, Error> { + + let uri_str = format!("{}/api/price_feed_ids", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<String>`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<String>`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// Get the set of price feeds. This endpoint fetches all price feeds from the Pyth network. It can be filtered by asset type and query string. +pub async fn price_feeds_metadata(configuration: &configuration::Configuration, query: Option<&str>, asset_type: Option) -> Result, Error> { + // add a prefix to parameters to efficiently prevent name collisions + let p_query = query; + let p_asset_type = asset_type; + + let uri_str = format!("{}/v2/price_feeds", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + if let Some(ref param_value) = p_query { + req_builder = req_builder.query(&[("query", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_asset_type { + req_builder = req_builder.query(&[("asset_type", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<models::PriceFeedMetadata>`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<models::PriceFeedMetadata>`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// SSE route handler for streaming price updates. The connection will automatically close after 24 hours to prevent resource leaks. Clients should implement reconnection logic to maintain continuous price updates. +pub async fn price_stream_sse_handler(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, allow_unordered: Option, benchmarks_only: Option, ignore_invalid_price_ids: Option) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_encoding = encoding; + let p_parsed = parsed; + let p_allow_unordered = allow_unordered; + let p_benchmarks_only = benchmarks_only; + let p_ignore_invalid_price_ids = ignore_invalid_price_ids; + + let uri_str = format!("{}/v2/updates/price/stream", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = match "multi" { + "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + if let Some(ref param_value) = p_encoding { + req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_parsed { + req_builder = req_builder.query(&[("parsed", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_allow_unordered { + req_builder = req_builder.query(&[("allow_unordered", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_benchmarks_only { + req_builder = req_builder.query(&[("benchmarks_only", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_ignore_invalid_price_ids { + req_builder = req_builder.query(&[("ignore_invalid_price_ids", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + +/// Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. +pub async fn timestamp_price_updates(configuration: &configuration::Configuration, publish_time: i64, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { + // add a prefix to parameters to efficiently prevent name collisions + let p_publish_time = publish_time; + let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_encoding = encoding; + let p_parsed = parsed; + let p_ignore_invalid_price_ids = ignore_invalid_price_ids; + + let uri_str = format!("{}/v2/updates/price/{publish_time}", configuration.base_path, publish_time=p_publish_time); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + req_builder = match "multi" { + "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + if let Some(ref param_value) = p_encoding { + req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_parsed { + req_builder = req_builder.query(&[("parsed", ¶m_value.to_string())]); + } + if let Some(ref param_value) = p_ignore_invalid_price_ids { + req_builder = req_builder.query(&[("ignore_invalid_price_ids", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), + } + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { status, content, entity })) + } +} + diff --git a/apps/hermes/client/rust/src/lib.rs b/apps/hermes/client/rust/src/lib.rs new file mode 100644 index 0000000000..e1520628d7 --- /dev/null +++ b/apps/hermes/client/rust/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(unused_imports)] +#![allow(clippy::too_many_arguments)] + +extern crate serde_repr; +extern crate serde; +extern crate serde_json; +extern crate url; +extern crate reqwest; + +pub mod apis; +pub mod models; diff --git a/apps/hermes/client/rust/src/models/asset_type.rs b/apps/hermes/client/rust/src/models/asset_type.rs new file mode 100644 index 0000000000..fa49e213c0 --- /dev/null +++ b/apps/hermes/client/rust/src/models/asset_type.rs @@ -0,0 +1,50 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum AssetType { + #[serde(rename = "crypto")] + Crypto, + #[serde(rename = "fx")] + Fx, + #[serde(rename = "equity")] + Equity, + #[serde(rename = "metal")] + Metal, + #[serde(rename = "rates")] + Rates, + #[serde(rename = "crypto_redemption_rate")] + CryptoRedemptionRate, + +} + +impl std::fmt::Display for AssetType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Crypto => write!(f, "crypto"), + Self::Fx => write!(f, "fx"), + Self::Equity => write!(f, "equity"), + Self::Metal => write!(f, "metal"), + Self::Rates => write!(f, "rates"), + Self::CryptoRedemptionRate => write!(f, "crypto_redemption_rate"), + } + } +} + +impl Default for AssetType { + fn default() -> AssetType { + Self::Crypto + } +} + diff --git a/apps/hermes/client/rust/src/models/binary_update.rs b/apps/hermes/client/rust/src/models/binary_update.rs new file mode 100644 index 0000000000..57bf2cfd2a --- /dev/null +++ b/apps/hermes/client/rust/src/models/binary_update.rs @@ -0,0 +1,30 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct BinaryUpdate { + #[serde(rename = "data")] + pub data: Vec, + #[serde(rename = "encoding")] + pub encoding: models::EncodingType, +} + +impl BinaryUpdate { + pub fn new(data: Vec, encoding: models::EncodingType) -> BinaryUpdate { + BinaryUpdate { + data, + encoding, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/encoding_type.rs b/apps/hermes/client/rust/src/models/encoding_type.rs new file mode 100644 index 0000000000..edbbf12e61 --- /dev/null +++ b/apps/hermes/client/rust/src/models/encoding_type.rs @@ -0,0 +1,38 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +/// +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum EncodingType { + #[serde(rename = "hex")] + Hex, + #[serde(rename = "base64")] + Base64, + +} + +impl std::fmt::Display for EncodingType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Hex => write!(f, "hex"), + Self::Base64 => write!(f, "base64"), + } + } +} + +impl Default for EncodingType { + fn default() -> EncodingType { + Self::Hex + } +} + diff --git a/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs b/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs new file mode 100644 index 0000000000..e009a6a3aa --- /dev/null +++ b/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs @@ -0,0 +1,27 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct GetVaaCcipResponse { + #[serde(rename = "data")] + pub data: String, +} + +impl GetVaaCcipResponse { + pub fn new(data: String) -> GetVaaCcipResponse { + GetVaaCcipResponse { + data, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/get_vaa_response.rs b/apps/hermes/client/rust/src/models/get_vaa_response.rs new file mode 100644 index 0000000000..2f587f9384 --- /dev/null +++ b/apps/hermes/client/rust/src/models/get_vaa_response.rs @@ -0,0 +1,31 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct GetVaaResponse { + #[serde(rename = "publishTime")] + pub publish_time: i64, + /// The VAA binary represented as a base64 string. + #[serde(rename = "vaa")] + pub vaa: String, +} + +impl GetVaaResponse { + pub fn new(publish_time: i64, vaa: String) -> GetVaaResponse { + GetVaaResponse { + publish_time, + vaa, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs b/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs new file mode 100644 index 0000000000..c47cca23b8 --- /dev/null +++ b/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs @@ -0,0 +1,30 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct LatestPublisherStakeCapsUpdateDataResponse { + #[serde(rename = "binary")] + pub binary: Box, + #[serde(rename = "parsed", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub parsed: Option>>, +} + +impl LatestPublisherStakeCapsUpdateDataResponse { + pub fn new(binary: models::BinaryUpdate) -> LatestPublisherStakeCapsUpdateDataResponse { + LatestPublisherStakeCapsUpdateDataResponse { + binary: Box::new(binary), + parsed: None, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/mod.rs b/apps/hermes/client/rust/src/models/mod.rs new file mode 100644 index 0000000000..7115b3451b --- /dev/null +++ b/apps/hermes/client/rust/src/models/mod.rs @@ -0,0 +1,34 @@ +pub mod asset_type; +pub use self::asset_type::AssetType; +pub mod binary_update; +pub use self::binary_update::BinaryUpdate; +pub mod encoding_type; +pub use self::encoding_type::EncodingType; +pub mod get_vaa_ccip_response; +pub use self::get_vaa_ccip_response::GetVaaCcipResponse; +pub mod get_vaa_response; +pub use self::get_vaa_response::GetVaaResponse; +pub mod latest_publisher_stake_caps_update_data_response; +pub use self::latest_publisher_stake_caps_update_data_response::LatestPublisherStakeCapsUpdateDataResponse; +pub mod parsed_price_feed_twap; +pub use self::parsed_price_feed_twap::ParsedPriceFeedTwap; +pub mod parsed_price_update; +pub use self::parsed_price_update::ParsedPriceUpdate; +pub mod parsed_publisher_stake_cap; +pub use self::parsed_publisher_stake_cap::ParsedPublisherStakeCap; +pub mod parsed_publisher_stake_caps_update; +pub use self::parsed_publisher_stake_caps_update::ParsedPublisherStakeCapsUpdate; +pub mod price_feed_metadata; +pub use self::price_feed_metadata::PriceFeedMetadata; +pub mod price_update; +pub use self::price_update::PriceUpdate; +pub mod rpc_price; +pub use self::rpc_price::RpcPrice; +pub mod rpc_price_feed; +pub use self::rpc_price_feed::RpcPriceFeed; +pub mod rpc_price_feed_metadata; +pub use self::rpc_price_feed_metadata::RpcPriceFeedMetadata; +pub mod rpc_price_feed_metadata_v2; +pub use self::rpc_price_feed_metadata_v2::RpcPriceFeedMetadataV2; +pub mod twaps_response; +pub use self::twaps_response::TwapsResponse; diff --git a/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs b/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs new file mode 100644 index 0000000000..120776b31c --- /dev/null +++ b/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs @@ -0,0 +1,42 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct ParsedPriceFeedTwap { + /// The % of slots where the network was down over the TWAP window. A value of zero indicates no slots were missed over the window, and a value of one indicates that every slot was missed over the window. This is a float value stored as a string to avoid precision loss. + #[serde(rename = "down_slots_ratio")] + pub down_slots_ratio: String, + /// The end unix timestamp of the window + #[serde(rename = "end_timestamp")] + pub end_timestamp: i64, + #[serde(rename = "id")] + pub id: String, + /// The start unix timestamp of the window + #[serde(rename = "start_timestamp")] + pub start_timestamp: i64, + #[serde(rename = "twap")] + pub twap: Box, +} + +impl ParsedPriceFeedTwap { + pub fn new(down_slots_ratio: String, end_timestamp: i64, id: String, start_timestamp: i64, twap: models::RpcPrice) -> ParsedPriceFeedTwap { + ParsedPriceFeedTwap { + down_slots_ratio, + end_timestamp, + id, + start_timestamp, + twap: Box::new(twap), + } + } +} + diff --git a/apps/hermes/client/rust/src/models/parsed_price_update.rs b/apps/hermes/client/rust/src/models/parsed_price_update.rs new file mode 100644 index 0000000000..3f557c9f5a --- /dev/null +++ b/apps/hermes/client/rust/src/models/parsed_price_update.rs @@ -0,0 +1,36 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct ParsedPriceUpdate { + #[serde(rename = "ema_price")] + pub ema_price: Box, + #[serde(rename = "id")] + pub id: String, + #[serde(rename = "metadata")] + pub metadata: Box, + #[serde(rename = "price")] + pub price: Box, +} + +impl ParsedPriceUpdate { + pub fn new(ema_price: models::RpcPrice, id: String, metadata: models::RpcPriceFeedMetadataV2, price: models::RpcPrice) -> ParsedPriceUpdate { + ParsedPriceUpdate { + ema_price: Box::new(ema_price), + id, + metadata: Box::new(metadata), + price: Box::new(price), + } + } +} + diff --git a/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs b/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs new file mode 100644 index 0000000000..cec953eb95 --- /dev/null +++ b/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs @@ -0,0 +1,30 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct ParsedPublisherStakeCap { + #[serde(rename = "cap")] + pub cap: i64, + #[serde(rename = "publisher")] + pub publisher: String, +} + +impl ParsedPublisherStakeCap { + pub fn new(cap: i64, publisher: String) -> ParsedPublisherStakeCap { + ParsedPublisherStakeCap { + cap, + publisher, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs b/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs new file mode 100644 index 0000000000..f140b388bf --- /dev/null +++ b/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs @@ -0,0 +1,27 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct ParsedPublisherStakeCapsUpdate { + #[serde(rename = "publisher_stake_caps")] + pub publisher_stake_caps: Vec, +} + +impl ParsedPublisherStakeCapsUpdate { + pub fn new(publisher_stake_caps: Vec) -> ParsedPublisherStakeCapsUpdate { + ParsedPublisherStakeCapsUpdate { + publisher_stake_caps, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/price_feed_metadata.rs b/apps/hermes/client/rust/src/models/price_feed_metadata.rs new file mode 100644 index 0000000000..936b624edd --- /dev/null +++ b/apps/hermes/client/rust/src/models/price_feed_metadata.rs @@ -0,0 +1,30 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct PriceFeedMetadata { + #[serde(rename = "attributes")] + pub attributes: std::collections::HashMap, + #[serde(rename = "id")] + pub id: String, +} + +impl PriceFeedMetadata { + pub fn new(attributes: std::collections::HashMap, id: String) -> PriceFeedMetadata { + PriceFeedMetadata { + attributes, + id, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/price_update.rs b/apps/hermes/client/rust/src/models/price_update.rs new file mode 100644 index 0000000000..f407fee60d --- /dev/null +++ b/apps/hermes/client/rust/src/models/price_update.rs @@ -0,0 +1,30 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct PriceUpdate { + #[serde(rename = "binary")] + pub binary: Box, + #[serde(rename = "parsed", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub parsed: Option>>, +} + +impl PriceUpdate { + pub fn new(binary: models::BinaryUpdate) -> PriceUpdate { + PriceUpdate { + binary: Box::new(binary), + parsed: None, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/rpc_price.rs b/apps/hermes/client/rust/src/models/rpc_price.rs new file mode 100644 index 0000000000..41067e7a1e --- /dev/null +++ b/apps/hermes/client/rust/src/models/rpc_price.rs @@ -0,0 +1,42 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +/// RpcPrice : A price with a degree of uncertainty at a certain time, represented as a price +- a confidence interval. The confidence interval roughly corresponds to the standard error of a normal distribution. Both the price and confidence are stored in a fixed-point numeric representation, `x * 10^expo`, where `expo` is the exponent. For example: +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct RpcPrice { + /// The confidence interval associated with the price, stored as a string to avoid precision loss + #[serde(rename = "conf")] + pub conf: String, + /// The exponent associated with both the price and confidence interval. Multiply those values by `10^expo` to get the real value. + #[serde(rename = "expo")] + pub expo: i32, + /// The price itself, stored as a string to avoid precision loss + #[serde(rename = "price")] + pub price: String, + /// When the price was published. The `publish_time` is a unix timestamp, i.e., the number of seconds since the Unix epoch (00:00:00 UTC on 1 Jan 1970). + #[serde(rename = "publish_time")] + pub publish_time: i64, +} + +impl RpcPrice { + /// A price with a degree of uncertainty at a certain time, represented as a price +- a confidence interval. The confidence interval roughly corresponds to the standard error of a normal distribution. Both the price and confidence are stored in a fixed-point numeric representation, `x * 10^expo`, where `expo` is the exponent. For example: + pub fn new(conf: String, expo: i32, price: String, publish_time: i64) -> RpcPrice { + RpcPrice { + conf, + expo, + price, + publish_time, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/rpc_price_feed.rs b/apps/hermes/client/rust/src/models/rpc_price_feed.rs new file mode 100644 index 0000000000..f877794d94 --- /dev/null +++ b/apps/hermes/client/rust/src/models/rpc_price_feed.rs @@ -0,0 +1,40 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct RpcPriceFeed { + #[serde(rename = "ema_price")] + pub ema_price: Box, + #[serde(rename = "id")] + pub id: String, + #[serde(rename = "metadata", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub metadata: Option>>, + #[serde(rename = "price")] + pub price: Box, + /// The VAA binary represented as a base64 string. + #[serde(rename = "vaa", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub vaa: Option>, +} + +impl RpcPriceFeed { + pub fn new(ema_price: models::RpcPrice, id: String, price: models::RpcPrice) -> RpcPriceFeed { + RpcPriceFeed { + ema_price: Box::new(ema_price), + id, + metadata: None, + price: Box::new(price), + vaa: None, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs new file mode 100644 index 0000000000..1fe72c1975 --- /dev/null +++ b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs @@ -0,0 +1,36 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct RpcPriceFeedMetadata { + #[serde(rename = "emitter_chain")] + pub emitter_chain: i32, + #[serde(rename = "prev_publish_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub prev_publish_time: Option>, + #[serde(rename = "price_service_receive_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub price_service_receive_time: Option>, + #[serde(rename = "slot", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub slot: Option>, +} + +impl RpcPriceFeedMetadata { + pub fn new(emitter_chain: i32) -> RpcPriceFeedMetadata { + RpcPriceFeedMetadata { + emitter_chain, + prev_publish_time: None, + price_service_receive_time: None, + slot: None, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs new file mode 100644 index 0000000000..cc3296b7b2 --- /dev/null +++ b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs @@ -0,0 +1,33 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct RpcPriceFeedMetadataV2 { + #[serde(rename = "prev_publish_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub prev_publish_time: Option>, + #[serde(rename = "proof_available_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub proof_available_time: Option>, + #[serde(rename = "slot", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub slot: Option>, +} + +impl RpcPriceFeedMetadataV2 { + pub fn new() -> RpcPriceFeedMetadataV2 { + RpcPriceFeedMetadataV2 { + prev_publish_time: None, + proof_available_time: None, + slot: None, + } + } +} + diff --git a/apps/hermes/client/rust/src/models/twaps_response.rs b/apps/hermes/client/rust/src/models/twaps_response.rs new file mode 100644 index 0000000000..64b074fcd6 --- /dev/null +++ b/apps/hermes/client/rust/src/models/twaps_response.rs @@ -0,0 +1,31 @@ +/* + * hermes + * + * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. + * + * The version of the OpenAPI document: 0.8.6 + * + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct TwapsResponse { + #[serde(rename = "binary")] + pub binary: Box, + /// The calculated TWAPs for each price ID + #[serde(rename = "parsed", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + pub parsed: Option>>, +} + +impl TwapsResponse { + pub fn new(binary: models::BinaryUpdate) -> TwapsResponse { + TwapsResponse { + binary: Box::new(binary), + parsed: None, + } + } +} + From 5e7c56b75d82a9dde89a10f9fd599e4e97b3488b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 22:59:41 +0000 Subject: [PATCH 02/12] feat: add examples for streaming price updates and fetching latest prices Co-Authored-By: Tejas Badadare --- .../client/rust/.openapi-generator-ignore | 1 + apps/hermes/client/rust/Cargo.lock | 357 +++++++++++++++++- apps/hermes/client/rust/Cargo.toml | 10 +- apps/hermes/client/rust/README.md | 22 ++ .../client/rust/examples/latest_prices.rs | 56 +++ .../client/rust/examples/price_stream.rs | 66 ++++ 6 files changed, 510 insertions(+), 2 deletions(-) create mode 100644 apps/hermes/client/rust/examples/latest_prices.rs create mode 100644 apps/hermes/client/rust/examples/price_stream.rs diff --git a/apps/hermes/client/rust/.openapi-generator-ignore b/apps/hermes/client/rust/.openapi-generator-ignore index 7484ee590a..08c3001435 100644 --- a/apps/hermes/client/rust/.openapi-generator-ignore +++ b/apps/hermes/client/rust/.openapi-generator-ignore @@ -21,3 +21,4 @@ #docs/*.md # Then explicitly reverse the ignore rule for a single file: #!docs/README.md +examples/ diff --git a/apps/hermes/client/rust/Cargo.lock b/apps/hermes/client/rust/Cargo.lock index cc676f482d..e6b484ab23 100644 --- a/apps/hermes/client/rust/Cargo.lock +++ b/apps/hermes/client/rust/Cargo.lock @@ -98,6 +98,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.41" @@ -260,6 +266,33 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", +] + [[package]] name = "gimli" version = "0.31.1" @@ -361,6 +394,24 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 0.26.11", +] + [[package]] name = "hyper-util" version = "0.1.11" @@ -590,6 +641,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.4" @@ -628,7 +685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -718,6 +775,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -727,6 +793,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys", +] + [[package]] name = "quote" version = "1.0.40" @@ -736,6 +857,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "redox_syscall" version = "0.5.12" @@ -759,6 +915,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "ipnet", "js-sys", @@ -768,26 +925,95 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-rustls", "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.26.11", "windows-registry", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -943,6 +1169,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.101" @@ -974,6 +1206,26 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.41" @@ -1015,6 +1267,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.45.0" @@ -1044,6 +1311,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tower" version = "0.5.2" @@ -1120,6 +1397,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" @@ -1152,6 +1435,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1233,6 +1525,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-core" version = "0.61.0" @@ -1449,6 +1769,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "writeable" version = "0.6.1" @@ -1479,6 +1808,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.6" @@ -1500,6 +1849,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerotrie" version = "0.2.2" diff --git a/apps/hermes/client/rust/Cargo.toml b/apps/hermes/client/rust/Cargo.toml index ea9c8a2b14..8f18b889c7 100644 --- a/apps/hermes/client/rust/Cargo.toml +++ b/apps/hermes/client/rust/Cargo.toml @@ -12,10 +12,18 @@ serde_with = { version = "3.8", default-features = false, features = ["base64", serde_json = "1.0" serde_repr = "0.1" url = "2.5" -reqwest = { version = "0.12", default-features = false, features = ["json", "multipart"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls"] } tokio = { version = "1.0", features = ["full"] } anyhow = "1.0" futures-util = "0.3" derive_more = { version = "1.0.0", features = ["from"] } base64 = "0.22.1" tracing = "0.1" + +[[example]] +name = "latest_prices" +path = "examples/latest_prices.rs" + +[[example]] +name = "price_stream" +path = "examples/price_stream.rs" diff --git a/apps/hermes/client/rust/README.md b/apps/hermes/client/rust/README.md index 1e5f1991b9..dcc64e2a31 100644 --- a/apps/hermes/client/rust/README.md +++ b/apps/hermes/client/rust/README.md @@ -36,6 +36,28 @@ async fn main() -> Result<(), Box> { } ``` +## Examples + +The package includes example applications that demonstrate how to use the client: + +### Fetching Latest Prices + +This example fetches and displays the latest prices for BTC/USD and ETH/USD: + +```bash +cargo run --example latest_prices +``` + +### Streaming Price Updates + +This example demonstrates how to subscribe to a real-time stream of price updates for BTC/USD and ETH/USD: + +```bash +cargo run --example price_stream +``` + +To run the examples, clone the repository and execute the commands from the `apps/hermes/client/rust` directory. + ## API Documentation For detailed API documentation, you can generate the documentation locally: diff --git a/apps/hermes/client/rust/examples/latest_prices.rs b/apps/hermes/client/rust/examples/latest_prices.rs new file mode 100644 index 0000000000..5fca65a1db --- /dev/null +++ b/apps/hermes/client/rust/examples/latest_prices.rs @@ -0,0 +1,56 @@ +use hermes_client::apis::configuration::Configuration; +use hermes_client::apis::rest_api; +use hermes_client::models::EncodingType; +use std::error::Error; + +const BTC_PRICE_FEED_ID: &str = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"; +const ETH_PRICE_FEED_ID: &str = "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let mut config = Configuration::new(); + config.base_path = "https://hermes.pyth.network".to_string(); + + let price_feed_ids = vec![ + BTC_PRICE_FEED_ID.to_string(), + ETH_PRICE_FEED_ID.to_string() + ]; + + let price_update = rest_api::latest_price_updates( + &config, + price_feed_ids, + Some(EncodingType::Base64), + Some(true), // parsed + Some(false) // ignore_invalid_price_ids + ).await?; + + println!("Latest Price Updates:"); + println!("===================="); + + if let Some(Some(parsed_updates)) = price_update.parsed { + for update in parsed_updates { + let price_feed_id = update.id; + let symbol = match price_feed_id.as_str() { + BTC_PRICE_FEED_ID => "BTC/USD", + ETH_PRICE_FEED_ID => "ETH/USD", + _ => "Unknown", + }; + + let price = &update.price; + let price_value = price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + let conf_value = price.conf.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + + println!( + "{}: ${:.2} (conf: ${:.2}, publish_time: {})", + symbol, + price_value, + conf_value, + price.publish_time + ); + } + } else { + println!("No parsed price data available"); + } + + Ok(()) +} diff --git a/apps/hermes/client/rust/examples/price_stream.rs b/apps/hermes/client/rust/examples/price_stream.rs new file mode 100644 index 0000000000..9a620df5c9 --- /dev/null +++ b/apps/hermes/client/rust/examples/price_stream.rs @@ -0,0 +1,66 @@ +use hermes_client::apis::configuration::Configuration; +use hermes_client::apis::rest_api; +use hermes_client::models::EncodingType; +use std::error::Error; +use std::time::Duration; +use tokio::time; + +const BTC_PRICE_FEED_ID: &str = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"; +const ETH_PRICE_FEED_ID: &str = "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let mut config = Configuration::new(); + config.base_path = "https://hermes.pyth.network".to_string(); + + let price_feed_ids = vec![ + BTC_PRICE_FEED_ID.to_string(), + ETH_PRICE_FEED_ID.to_string() + ]; + + println!("Starting price stream for BTC/USD and ETH/USD..."); + println!("Press Ctrl+C to exit"); + println!("===================="); + + let mut interval = time::interval(Duration::from_secs(2)); + + loop { + interval.tick().await; + + match rest_api::latest_price_updates( + &config, + price_feed_ids.clone(), + Some(EncodingType::Base64), + Some(true), // parsed + Some(false) // ignore_invalid_price_ids + ).await { + Ok(price_update) => { + if let Some(Some(parsed_updates)) = price_update.parsed { + for update in parsed_updates { + let price_feed_id = update.id; + let symbol = match price_feed_id.as_str() { + BTC_PRICE_FEED_ID => "BTC/USD", + ETH_PRICE_FEED_ID => "ETH/USD", + _ => "Unknown", + }; + + let price = &update.price; + let price_value = price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + let conf_value = price.conf.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + + println!( + "{}: ${:.2} (conf: ${:.2}, publish_time: {})", + symbol, + price_value, + conf_value, + price.publish_time + ); + } + } + }, + Err(e) => { + eprintln!("Error fetching price update: {}", e); + } + } + } +} From 3c17a89cff8bd18936f60e472a066b223ea91bf7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 17:09:50 +0000 Subject: [PATCH 03/12] feat: implement true SSE streaming client for Hermes Co-Authored-By: Tejas Badadare --- .../client/rust/.openapi-generator-ignore | 6 + apps/hermes/client/rust/Cargo.lock | 56 +++++++++ apps/hermes/client/rust/Cargo.toml | 12 +- .../client/rust/examples/price_stream.rs | 77 ++++++------ apps/hermes/client/rust/src/lib.rs | 4 + apps/hermes/client/rust/src/streaming.rs | 112 ++++++++++++++++++ 6 files changed, 225 insertions(+), 42 deletions(-) create mode 100644 apps/hermes/client/rust/src/streaming.rs diff --git a/apps/hermes/client/rust/.openapi-generator-ignore b/apps/hermes/client/rust/.openapi-generator-ignore index 08c3001435..a09a0d6007 100644 --- a/apps/hermes/client/rust/.openapi-generator-ignore +++ b/apps/hermes/client/rust/.openapi-generator-ignore @@ -21,4 +21,10 @@ #docs/*.md # Then explicitly reverse the ignore rule for a single file: #!docs/README.md + +# Exclude the autogenerated SSE client implementation +# This will be replaced with a custom implementation that properly handles streaming +src/apis/rest_api.rs:price_stream_sse_handler + +# Exclude examples directory to prevent overwriting custom examples examples/ diff --git a/apps/hermes/client/rust/Cargo.lock b/apps/hermes/client/rust/Cargo.lock index e6b484ab23..b07e3effd4 100644 --- a/apps/hermes/client/rust/Cargo.lock +++ b/apps/hermes/client/rust/Cargo.lock @@ -235,6 +235,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-macro" version = "0.3.31" @@ -246,6 +252,12 @@ dependencies = [ "syn", ] +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + [[package]] name = "futures-task" version = "0.3.31" @@ -259,8 +271,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -325,6 +340,7 @@ dependencies = [ "serde_repr", "serde_with", "tokio", + "tokio-stream", "tracing", "url", ] @@ -935,11 +951,13 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-rustls", + "tokio-util", "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.26.11", "windows-registry", @@ -1321,6 +1339,31 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.2" @@ -1515,6 +1558,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.77" diff --git a/apps/hermes/client/rust/Cargo.toml b/apps/hermes/client/rust/Cargo.toml index 8f18b889c7..21ea576941 100644 --- a/apps/hermes/client/rust/Cargo.toml +++ b/apps/hermes/client/rust/Cargo.toml @@ -5,6 +5,11 @@ authors = ["Pyth Network Contributors"] description = "A Rust client for Pyth Hermes price service" license = "Apache-2.0" edition = "2021" +repository = "https://github.com/pyth-network/pyth-crosschain" +documentation = "https://docs.rs/hermes-client" +readme = "README.md" +keywords = ["pyth", "price", "oracle", "defi", "finance"] +categories = ["api-bindings", "finance"] [dependencies] serde = { version = "1.0", features = ["derive"] } @@ -12,8 +17,9 @@ serde_with = { version = "3.8", default-features = false, features = ["base64", serde_json = "1.0" serde_repr = "0.1" url = "2.5" -reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls", "stream"] } tokio = { version = "1.0", features = ["full"] } +tokio-stream = { version = "0.1", features = ["sync"] } anyhow = "1.0" futures-util = "0.3" derive_more = { version = "1.0.0", features = ["from"] } @@ -27,3 +33,7 @@ path = "examples/latest_prices.rs" [[example]] name = "price_stream" path = "examples/price_stream.rs" + +[[example]] +name = "sse_price_stream" +path = "examples/sse_price_stream.rs" diff --git a/apps/hermes/client/rust/examples/price_stream.rs b/apps/hermes/client/rust/examples/price_stream.rs index 9a620df5c9..cf15b772fe 100644 --- a/apps/hermes/client/rust/examples/price_stream.rs +++ b/apps/hermes/client/rust/examples/price_stream.rs @@ -1,9 +1,7 @@ -use hermes_client::apis::configuration::Configuration; -use hermes_client::apis::rest_api; -use hermes_client::models::EncodingType; +use futures_util::stream::StreamExt; +use hermes_client::Configuration; +use hermes_client::create_price_update_stream; use std::error::Error; -use std::time::Duration; -use tokio::time; const BTC_PRICE_FEED_ID: &str = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"; const ETH_PRICE_FEED_ID: &str = "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; @@ -18,49 +16,46 @@ async fn main() -> Result<(), Box> { ETH_PRICE_FEED_ID.to_string() ]; - println!("Starting price stream for BTC/USD and ETH/USD..."); + println!("Starting SSE price stream for BTC/USD and ETH/USD..."); println!("Press Ctrl+C to exit"); println!("===================="); - let mut interval = time::interval(Duration::from_secs(2)); + let mut stream = create_price_update_stream( + &config, + price_feed_ids, + None, // default encoding (base64) + None, // default allow_unordered + None, // default benchmarks_only + None // default ignore_invalid_price_ids + ).await?; - loop { - interval.tick().await; - - match rest_api::latest_price_updates( - &config, - price_feed_ids.clone(), - Some(EncodingType::Base64), - Some(true), // parsed - Some(false) // ignore_invalid_price_ids - ).await { - Ok(price_update) => { - if let Some(Some(parsed_updates)) = price_update.parsed { - for update in parsed_updates { - let price_feed_id = update.id; - let symbol = match price_feed_id.as_str() { - BTC_PRICE_FEED_ID => "BTC/USD", - ETH_PRICE_FEED_ID => "ETH/USD", - _ => "Unknown", - }; - - let price = &update.price; - let price_value = price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); - let conf_value = price.conf.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); - - println!( - "{}: ${:.2} (conf: ${:.2}, publish_time: {})", - symbol, - price_value, - conf_value, - price.publish_time - ); - } - } + while let Some(result) = stream.next().await { + match result { + Ok(update) => { + let price_feed_id = &update.id; + let symbol = match price_feed_id.as_str() { + BTC_PRICE_FEED_ID => "BTC/USD", + ETH_PRICE_FEED_ID => "ETH/USD", + _ => "Unknown", + }; + + let price = &update.price; + let price_value = price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + let conf_value = price.conf.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + + println!( + "{}: ${:.2} (conf: ${:.2}, publish_time: {})", + symbol, + price_value, + conf_value, + price.publish_time + ); }, Err(e) => { - eprintln!("Error fetching price update: {}", e); + eprintln!("Error: {}", e); } } } + + Ok(()) } diff --git a/apps/hermes/client/rust/src/lib.rs b/apps/hermes/client/rust/src/lib.rs index e1520628d7..824ded5045 100644 --- a/apps/hermes/client/rust/src/lib.rs +++ b/apps/hermes/client/rust/src/lib.rs @@ -9,3 +9,7 @@ extern crate reqwest; pub mod apis; pub mod models; +pub mod streaming; + +pub use crate::apis::configuration::Configuration; +pub use crate::streaming::create_price_update_stream; diff --git a/apps/hermes/client/rust/src/streaming.rs b/apps/hermes/client/rust/src/streaming.rs new file mode 100644 index 0000000000..6400b1eb08 --- /dev/null +++ b/apps/hermes/client/rust/src/streaming.rs @@ -0,0 +1,112 @@ +use crate::apis::configuration::Configuration; +use crate::models::{EncodingType, ParsedPriceUpdate, PriceUpdate}; +use futures_util::stream::{Stream, StreamExt}; +use std::error::Error; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; + +pub async fn create_price_update_stream( + config: &Configuration, + price_feed_ids: Vec, + encoding: Option, + allow_unordered: Option, + benchmarks_only: Option, + ignore_invalid_price_ids: Option, +) -> Result>>, Box> { + let base_url = format!("{}/v2/updates/price/stream", config.base_path); + let mut url = reqwest::Url::parse(&base_url)?; + + let mut query_pairs = url.query_pairs_mut(); + for id in &price_feed_ids { + query_pairs.append_pair("ids[]", id); + } + + if let Some(enc) = encoding { + query_pairs.append_pair("encoding", &enc.to_string()); + } else { + query_pairs.append_pair("encoding", "base64"); + } + + query_pairs.append_pair("parsed", "true"); + + if let Some(allow) = allow_unordered { + query_pairs.append_pair("allow_unordered", &allow.to_string()); + } + + if let Some(benchmarks) = benchmarks_only { + query_pairs.append_pair("benchmarks_only", &benchmarks.to_string()); + } + + if let Some(ignore) = ignore_invalid_price_ids { + query_pairs.append_pair("ignore_invalid_price_ids", &ignore.to_string()); + } + + drop(query_pairs); + + let (tx, rx) = mpsc::channel(100); + + tokio::spawn(async move { + let client = reqwest::Client::new(); + + let res = match client.get(url) + .header("Accept", "text/event-stream") + .send() + .await { + Ok(res) => res, + Err(e) => { + let _ = tx.send(Err(Box::new(e) as Box)).await; + return; + } + }; + + if !res.status().is_success() { + let _ = tx.send(Err(format!("Failed to connect to SSE endpoint: {}", res.status()).into())).await; + return; + } + + let mut buffer = String::new(); + let mut stream = res.bytes_stream(); + + while let Some(chunk_result) = stream.next().await { + match chunk_result { + Ok(chunk) => { + if let Ok(text) = String::from_utf8(chunk.to_vec()) { + buffer.push_str(&text); + + while let Some(pos) = buffer.find("\n\n") { + let event = buffer[..pos].to_string(); + buffer = buffer[pos + 2..].to_string(); + + if let Some(data_line) = event.lines().find(|line| line.starts_with("data:")) { + let data = data_line.trim_start_matches("data:").trim(); + + match serde_json::from_str::(data) { + Ok(price_update) => { + if let Some(Some(parsed_updates)) = price_update.parsed { + for update in parsed_updates { + if tx.send(Ok(update)).await.is_err() { + return; + } + } + } + }, + Err(e) => { + let _ = tx.send(Err(Box::new(e) as Box)).await; + } + } + } + } + } + }, + Err(e) => { + let _ = tx.send(Err(Box::new(e) as Box)).await; + break; + } + } + } + }); + + let stream = ReceiverStream::new(rx); + + Ok(stream) +} From b0afa97864f45762850704d00902323c8b63b30e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 18:06:05 +0000 Subject: [PATCH 04/12] refactor: update streaming implementation to use eventsource crate Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/Cargo.lock | 28 ++++++ apps/hermes/client/rust/Cargo.toml | 1 + apps/hermes/client/rust/src/streaming.rs | 121 +++++++++++++---------- 3 files changed, 97 insertions(+), 53 deletions(-) diff --git a/apps/hermes/client/rust/Cargo.lock b/apps/hermes/client/rust/Cargo.lock index b07e3effd4..747ee67f8c 100644 --- a/apps/hermes/client/rust/Cargo.lock +++ b/apps/hermes/client/rust/Cargo.lock @@ -205,6 +205,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + [[package]] name = "fnv" version = "1.0.7" @@ -333,6 +344,7 @@ dependencies = [ "anyhow", "base64", "derive_more", + "eventsource-stream", "futures-util", "reqwest", "serde", @@ -685,6 +697,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.8" @@ -705,6 +723,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-conv" version = "0.1.0" diff --git a/apps/hermes/client/rust/Cargo.toml b/apps/hermes/client/rust/Cargo.toml index 21ea576941..7ba59abc55 100644 --- a/apps/hermes/client/rust/Cargo.toml +++ b/apps/hermes/client/rust/Cargo.toml @@ -25,6 +25,7 @@ futures-util = "0.3" derive_more = { version = "1.0.0", features = ["from"] } base64 = "0.22.1" tracing = "0.1" +eventsource-stream = "0.2.3" [[example]] name = "latest_prices" diff --git a/apps/hermes/client/rust/src/streaming.rs b/apps/hermes/client/rust/src/streaming.rs index 6400b1eb08..2103de3a61 100644 --- a/apps/hermes/client/rust/src/streaming.rs +++ b/apps/hermes/client/rust/src/streaming.rs @@ -1,9 +1,14 @@ use crate::apis::configuration::Configuration; use crate::models::{EncodingType, ParsedPriceUpdate, PriceUpdate}; +use eventsource_stream::EventStream; use futures_util::stream::{Stream, StreamExt}; use std::error::Error; -use tokio::sync::mpsc; -use tokio_stream::wrappers::ReceiverStream; +use std::pin::Pin; +use futures_util::task::{Context, Poll}; +use std::collections::VecDeque; +use futures_util::stream::FusedStream; +use std::sync::Arc; +use std::sync::Mutex; pub async fn create_price_update_stream( config: &Configuration, @@ -43,70 +48,80 @@ pub async fn create_price_update_stream( drop(query_pairs); - let (tx, rx) = mpsc::channel(100); + let client = reqwest::Client::new(); + + let res = client.get(url) + .header("Accept", "text/event-stream") + .send() + .await?; + + if !res.status().is_success() { + return Err(format!("Failed to connect to SSE endpoint: {}", res.status()).into()); + } + + let stream = res.bytes_stream(); + let event_stream = EventStream::new(stream); + + let buffer = Arc::new(Mutex::new(VecDeque::new())); + let buffer_clone = buffer.clone(); tokio::spawn(async move { - let client = reqwest::Client::new(); - - let res = match client.get(url) - .header("Accept", "text/event-stream") - .send() - .await { - Ok(res) => res, - Err(e) => { - let _ = tx.send(Err(Box::new(e) as Box)).await; - return; - } - }; - - if !res.status().is_success() { - let _ = tx.send(Err(format!("Failed to connect to SSE endpoint: {}", res.status()).into())).await; - return; - } - - let mut buffer = String::new(); - let mut stream = res.bytes_stream(); + let mut stream = event_stream; - while let Some(chunk_result) = stream.next().await { - match chunk_result { - Ok(chunk) => { - if let Ok(text) = String::from_utf8(chunk.to_vec()) { - buffer.push_str(&text); - - while let Some(pos) = buffer.find("\n\n") { - let event = buffer[..pos].to_string(); - buffer = buffer[pos + 2..].to_string(); - - if let Some(data_line) = event.lines().find(|line| line.starts_with("data:")) { - let data = data_line.trim_start_matches("data:").trim(); - - match serde_json::from_str::(data) { - Ok(price_update) => { - if let Some(Some(parsed_updates)) = price_update.parsed { - for update in parsed_updates { - if tx.send(Ok(update)).await.is_err() { - return; - } - } - } - }, - Err(e) => { - let _ = tx.send(Err(Box::new(e) as Box)).await; - } + while let Some(event_result) = stream.next().await { + match event_result { + Ok(event) => { + if !event.event.is_empty() && event.event != "message" { + continue; + } + + match serde_json::from_str::(&event.data) { + Ok(price_update) => { + if let Some(Some(parsed_updates)) = price_update.parsed { + let mut buffer = buffer.lock().unwrap(); + for update in parsed_updates { + buffer.push_back(Ok(update)); } } + }, + Err(e) => { + let mut buffer = buffer.lock().unwrap(); + buffer.push_back(Err(format!("Failed to parse price update: {}", e).into())); } } }, Err(e) => { - let _ = tx.send(Err(Box::new(e) as Box)).await; - break; + let mut buffer = buffer.lock().unwrap(); + buffer.push_back(Err(format!("Error in SSE stream: {}", e).into())); } } } }); - let stream = ReceiverStream::new(rx); + Ok(PriceUpdateStream { buffer: buffer_clone }) +} + +struct PriceUpdateStream { + buffer: Arc>>>>, +} + +impl Stream for PriceUpdateStream { + type Item = Result>; - Ok(stream) + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut buffer = self.buffer.lock().unwrap(); + + if let Some(item) = buffer.pop_front() { + Poll::Ready(Some(item)) + } else { + cx.waker().wake_by_ref(); + Poll::Pending + } + } +} + +impl FusedStream for PriceUpdateStream { + fn is_terminated(&self) -> bool { + false + } } From f341b4f7c72b5b891d45b6fd500216520211d9bb Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 19:00:13 +0000 Subject: [PATCH 05/12] refactor: replace streaming implementation with official version Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/src/models/mod.rs | 2 + .../client/rust/src/models/sse_event.rs | 37 ++++++ apps/hermes/client/rust/src/streaming.rs | 124 +++++++----------- 3 files changed, 87 insertions(+), 76 deletions(-) create mode 100644 apps/hermes/client/rust/src/models/sse_event.rs diff --git a/apps/hermes/client/rust/src/models/mod.rs b/apps/hermes/client/rust/src/models/mod.rs index 7115b3451b..2522248a67 100644 --- a/apps/hermes/client/rust/src/models/mod.rs +++ b/apps/hermes/client/rust/src/models/mod.rs @@ -30,5 +30,7 @@ pub mod rpc_price_feed_metadata; pub use self::rpc_price_feed_metadata::RpcPriceFeedMetadata; pub mod rpc_price_feed_metadata_v2; pub use self::rpc_price_feed_metadata_v2::RpcPriceFeedMetadataV2; +pub mod sse_event; +pub use self::sse_event::SseEvent; pub mod twaps_response; pub use self::twaps_response::TwapsResponse; diff --git a/apps/hermes/client/rust/src/models/sse_event.rs b/apps/hermes/client/rust/src/models/sse_event.rs new file mode 100644 index 0000000000..a1adbd5290 --- /dev/null +++ b/apps/hermes/client/rust/src/models/sse_event.rs @@ -0,0 +1,37 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use super::parsed_price_update::ParsedPriceUpdate; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BinaryData { + #[serde(rename = "encoding")] + pub encoding: String, + + #[serde(rename = "data")] + pub data: Vec, + + #[serde(flatten)] + pub additional_properties: HashMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SseEvent { + #[serde(rename = "binary", skip_serializing_if = "Option::is_none")] + pub binary: Option, + + #[serde(rename = "parsed", skip_serializing_if = "Option::is_none")] + pub parsed: Option>, + + #[serde(flatten)] + pub additional_properties: HashMap, +} + +impl SseEvent { + pub fn new() -> SseEvent { + SseEvent { + binary: None, + parsed: None, + additional_properties: HashMap::new(), + } + } +} diff --git a/apps/hermes/client/rust/src/streaming.rs b/apps/hermes/client/rust/src/streaming.rs index 2103de3a61..3b539749b3 100644 --- a/apps/hermes/client/rust/src/streaming.rs +++ b/apps/hermes/client/rust/src/streaming.rs @@ -1,14 +1,8 @@ + use crate::apis::configuration::Configuration; use crate::models::{EncodingType, ParsedPriceUpdate, PriceUpdate}; -use eventsource_stream::EventStream; use futures_util::stream::{Stream, StreamExt}; use std::error::Error; -use std::pin::Pin; -use futures_util::task::{Context, Poll}; -use std::collections::VecDeque; -use futures_util::stream::FusedStream; -use std::sync::Arc; -use std::sync::Mutex; pub async fn create_price_update_stream( config: &Configuration, @@ -20,108 +14,86 @@ pub async fn create_price_update_stream( ) -> Result>>, Box> { let base_url = format!("{}/v2/updates/price/stream", config.base_path); let mut url = reqwest::Url::parse(&base_url)?; - + let mut query_pairs = url.query_pairs_mut(); for id in &price_feed_ids { query_pairs.append_pair("ids[]", id); } - + if let Some(enc) = encoding { query_pairs.append_pair("encoding", &enc.to_string()); } else { query_pairs.append_pair("encoding", "base64"); } - + query_pairs.append_pair("parsed", "true"); - + if let Some(allow) = allow_unordered { query_pairs.append_pair("allow_unordered", &allow.to_string()); } - + if let Some(benchmarks) = benchmarks_only { query_pairs.append_pair("benchmarks_only", &benchmarks.to_string()); } - + if let Some(ignore) = ignore_invalid_price_ids { query_pairs.append_pair("ignore_invalid_price_ids", &ignore.to_string()); } - + drop(query_pairs); - - let client = reqwest::Client::new(); - - let res = client.get(url) + + let client = reqwest::Client::builder() + .build()?; + + let response = client.get(url) .header("Accept", "text/event-stream") .send() .await?; - - if !res.status().is_success() { - return Err(format!("Failed to connect to SSE endpoint: {}", res.status()).into()); + + if !response.status().is_success() { + return Err(format!("Failed to connect to SSE endpoint: {}", response.status()).into()); } - - let stream = res.bytes_stream(); - let event_stream = EventStream::new(stream); - - let buffer = Arc::new(Mutex::new(VecDeque::new())); - let buffer_clone = buffer.clone(); - - tokio::spawn(async move { - let mut stream = event_stream; - - while let Some(event_result) = stream.next().await { + + let stream = response.bytes_stream(); + + let sse_stream = eventsource_stream::EventStream::new(stream) + .map(move |event_result| { match event_result { Ok(event) => { - if !event.event.is_empty() && event.event != "message" { - continue; + if event.event != "message" { + return Err(format!("Unexpected event type: {}", event.event).into()); } - - match serde_json::from_str::(&event.data) { - Ok(price_update) => { - if let Some(Some(parsed_updates)) = price_update.parsed { - let mut buffer = buffer.lock().unwrap(); - for update in parsed_updates { - buffer.push_back(Ok(update)); - } + + let data = &event.data; + + println!("Received SSE data: {}", data); + + match serde_json::from_str::(data) { + Ok(sse_event) => { + if let Some(parsed_updates) = sse_event.parsed { + let stream = parsed_updates.into_iter() + .map(|update| Ok(update)) + .collect::>>>(); + Ok(futures_util::stream::iter(stream)) + } else { + Err("No parsed price updates in the response".into()) } }, - Err(e) => { - let mut buffer = buffer.lock().unwrap(); - buffer.push_back(Err(format!("Failed to parse price update: {}", e).into())); - } + Err(e) => Err(format!("Failed to parse price update: {}", e).into()), } }, + Err(e) => Err(format!("Error in SSE stream: {}", e).into()), + } + }) + .flat_map(|result| { + match result { + Ok(stream) => stream, Err(e) => { - let mut buffer = buffer.lock().unwrap(); - buffer.push_back(Err(format!("Error in SSE stream: {}", e).into())); + let err = e; + futures_util::stream::iter(vec![Err(err)]) } } - } - }); - - Ok(PriceUpdateStream { buffer: buffer_clone }) -} + }); -struct PriceUpdateStream { - buffer: Arc>>>>, -} - -impl Stream for PriceUpdateStream { - type Item = Result>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut buffer = self.buffer.lock().unwrap(); - - if let Some(item) = buffer.pop_front() { - Poll::Ready(Some(item)) - } else { - cx.waker().wake_by_ref(); - Poll::Pending - } - } -} - -impl FusedStream for PriceUpdateStream { - fn is_terminated(&self) -> bool { - false - } + Ok(sse_stream) } From e317e589b165c073b878a129c23557c8bc5b10b2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 11:55:56 +0000 Subject: [PATCH 06/12] fix: remove reference to non-existent example file Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/hermes/client/rust/Cargo.toml b/apps/hermes/client/rust/Cargo.toml index 7ba59abc55..e0d6651fc5 100644 --- a/apps/hermes/client/rust/Cargo.toml +++ b/apps/hermes/client/rust/Cargo.toml @@ -34,7 +34,3 @@ path = "examples/latest_prices.rs" [[example]] name = "price_stream" path = "examples/price_stream.rs" - -[[example]] -name = "sse_price_stream" -path = "examples/sse_price_stream.rs" From d1878cfe3c57fb2f55d2c692fe342e69d7cfc917 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 11:59:20 +0000 Subject: [PATCH 07/12] fix: rename awkward parameter names in rest_api.rs Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/src/apis/rest_api.rs | 33 ++++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/apps/hermes/client/rust/src/apis/rest_api.rs b/apps/hermes/client/rust/src/apis/rest_api.rs index 803761638b..dafebbf4da 100644 --- a/apps/hermes/client/rust/src/apis/rest_api.rs +++ b/apps/hermes/client/rust/src/apis/rest_api.rs @@ -232,9 +232,9 @@ pub async fn get_vaa_ccip(configuration: &configuration::Configuration, data: st } /// **Deprecated: use /v2/updates/price/latest instead** Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. -pub async fn latest_price_feeds(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec, verbose: Option, binary: Option) -> Result, Error> { +pub async fn latest_price_feeds(configuration: &configuration::Configuration, ids: Vec, verbose: Option, binary: Option) -> Result, Error> { // add a prefix to parameters to efficiently prevent name collisions - let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_ids = ids; let p_verbose = verbose; let p_binary = binary; @@ -242,8 +242,8 @@ pub async fn latest_price_feeds(configuration: &configuration::Configuration, id let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), }; if let Some(ref param_value) = p_verbose { req_builder = req_builder.query(&[("verbose", ¶m_value.to_string())]); @@ -281,9 +281,9 @@ pub async fn latest_price_feeds(configuration: &configuration::Configuration, id } /// Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. -pub async fn latest_price_updates(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn latest_price_updates(configuration: &configuration::Configuration, ids: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { // add a prefix to parameters to efficiently prevent name collisions - let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_ids = ids; let p_encoding = encoding; let p_parsed = parsed; let p_ignore_invalid_price_ids = ignore_invalid_price_ids; @@ -292,8 +292,8 @@ pub async fn latest_price_updates(configuration: &configuration::Configuration, let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -552,9 +552,9 @@ pub async fn price_feeds_metadata(configuration: &configuration::Configuration, } /// SSE route handler for streaming price updates. The connection will automatically close after 24 hours to prevent resource leaks. Clients should implement reconnection logic to maintain continuous price updates. -pub async fn price_stream_sse_handler(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, allow_unordered: Option, benchmarks_only: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn price_stream_sse_handler(configuration: &configuration::Configuration, ids: Vec, encoding: Option, parsed: Option, allow_unordered: Option, benchmarks_only: Option, ignore_invalid_price_ids: Option) -> Result> { // add a prefix to parameters to efficiently prevent name collisions - let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_ids = ids; let p_encoding = encoding; let p_parsed = parsed; let p_allow_unordered = allow_unordered; @@ -565,8 +565,8 @@ pub async fn price_stream_sse_handler(configuration: &configuration::Configurati let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -613,10 +613,10 @@ pub async fn price_stream_sse_handler(configuration: &configuration::Configurati } /// Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. -pub async fn timestamp_price_updates(configuration: &configuration::Configuration, publish_time: i64, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn timestamp_price_updates(configuration: &configuration::Configuration, publish_time: i64, ids: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_publish_time = publish_time; - let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_ids = ids; let p_encoding = encoding; let p_parsed = parsed; let p_ignore_invalid_price_ids = ignore_invalid_price_ids; @@ -625,8 +625,8 @@ pub async fn timestamp_price_updates(configuration: &configuration::Configuratio let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -665,4 +665,3 @@ pub async fn timestamp_price_updates(configuration: &configuration::Configuratio Err(Error::ResponseError(ResponseContent { status, content, entity })) } } - From d10e5e976e4ba6c6a5db59c49e24210d014b26bc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:03:03 +0000 Subject: [PATCH 08/12] ci: add workflow for Hermes Rust client Co-Authored-By: Tejas Badadare --- .github/workflows/ci-hermes-client-rust.yml | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/ci-hermes-client-rust.yml diff --git a/.github/workflows/ci-hermes-client-rust.yml b/.github/workflows/ci-hermes-client-rust.yml new file mode 100644 index 0000000000..76e8bb5a4a --- /dev/null +++ b/.github/workflows/ci-hermes-client-rust.yml @@ -0,0 +1,41 @@ +name: Check Hermes Rust Client + +on: + pull_request: + paths: + - .github/workflows/ci-hermes-client-rust.yml + - apps/hermes/client/rust/** + push: + branches: [main] +jobs: + test: + runs-on: ubuntu-latest + defaults: + run: + working-directory: apps/hermes/client/rust + steps: + - uses: actions/checkout@v2 + - uses: Swatinem/rust-cache@v2 + with: + workspaces: "apps/hermes/client/rust -> target" + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.82.0 + components: rustfmt, clippy + override: true + - name: Format check + run: cargo fmt --all -- --check + if: success() || failure() + - name: Clippy check + run: cargo clippy --tests -- --deny warnings + if: success() || failure() + - name: Build check + run: cargo build + if: success() || failure() + - name: Run tests + run: cargo test + if: success() || failure() + - name: Build examples + run: cargo build --examples + if: success() || failure() From f3bd3ad0b17552084fdda351430900da9e09b3a2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:10:53 +0000 Subject: [PATCH 09/12] fix: resolve clippy linting issues in Hermes Rust client Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/src/apis/mod.rs | 6 +-- apps/hermes/client/rust/src/apis/rest_api.rs | 48 +++++++++---------- .../client/rust/src/models/asset_type.rs | 1 - .../client/rust/src/models/encoding_type.rs | 1 - .../client/rust/src/models/sse_event.rs | 6 +++ apps/hermes/client/rust/src/streaming.rs | 7 +-- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/apps/hermes/client/rust/src/apis/mod.rs b/apps/hermes/client/rust/src/apis/mod.rs index 9298e979ca..cdffe1bf82 100644 --- a/apps/hermes/client/rust/src/apis/mod.rs +++ b/apps/hermes/client/rust/src/apis/mod.rs @@ -102,11 +102,11 @@ enum ContentType { impl From<&str> for ContentType { fn from(content_type: &str) -> Self { if content_type.starts_with("application") && content_type.contains("json") { - return Self::Json; + Self::Json } else if content_type.starts_with("text/plain") { - return Self::Text; + Self::Text } else { - return Self::Unsupported(content_type.to_string()); + Self::Unsupported(content_type.to_string()) } } } diff --git a/apps/hermes/client/rust/src/apis/rest_api.rs b/apps/hermes/client/rust/src/apis/rest_api.rs index dafebbf4da..b04b4be017 100644 --- a/apps/hermes/client/rust/src/apis/rest_api.rs +++ b/apps/hermes/client/rust/src/apis/rest_api.rs @@ -143,8 +143,8 @@ pub async fn get_price_feed(configuration: &configuration::Configuration, id: &s let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RpcPriceFeed`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::RpcPriceFeed`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RpcPriceFeed`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::RpcPriceFeed`")))), } } else { let content = resp.text().await?; @@ -183,8 +183,8 @@ pub async fn get_vaa(configuration: &configuration::Configuration, id: &str, pub let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetVaaResponse`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetVaaResponse`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetVaaResponse`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetVaaResponse`")))), } } else { let content = resp.text().await?; @@ -221,8 +221,8 @@ pub async fn get_vaa_ccip(configuration: &configuration::Configuration, data: st let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetVaaCcipResponse`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetVaaCcipResponse`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetVaaCcipResponse`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetVaaCcipResponse`")))), } } else { let content = resp.text().await?; @@ -270,8 +270,8 @@ pub async fn latest_price_feeds(configuration: &configuration::Configuration, id let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<models::RpcPriceFeed>`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<models::RpcPriceFeed>`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<models::RpcPriceFeed>`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<models::RpcPriceFeed>`")))), } } else { let content = resp.text().await?; @@ -323,8 +323,8 @@ pub async fn latest_price_updates(configuration: &configuration::Configuration, let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), } } else { let content = resp.text().await?; @@ -367,8 +367,8 @@ pub async fn latest_publisher_stake_caps(configuration: &configuration::Configur let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LatestPublisherStakeCapsUpdateDataResponse`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::LatestPublisherStakeCapsUpdateDataResponse`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LatestPublisherStakeCapsUpdateDataResponse`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::LatestPublisherStakeCapsUpdateDataResponse`")))), } } else { let content = resp.text().await?; @@ -421,8 +421,8 @@ pub async fn latest_twaps(configuration: &configuration::Configuration, window_s let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TwapsResponse`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::TwapsResponse`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TwapsResponse`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::TwapsResponse`")))), } } else { let content = resp.text().await?; @@ -462,8 +462,8 @@ pub async fn latest_vaas(configuration: &configuration::Configuration, ids_left_ let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<String>`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<String>`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<String>`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<String>`")))), } } else { let content = resp.text().await?; @@ -497,8 +497,8 @@ pub async fn price_feed_ids(configuration: &configuration::Configuration, ) -> R let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<String>`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<String>`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<String>`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<String>`")))), } } else { let content = resp.text().await?; @@ -541,8 +541,8 @@ pub async fn price_feeds_metadata(configuration: &configuration::Configuration, let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<models::PriceFeedMetadata>`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<models::PriceFeedMetadata>`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec<models::PriceFeedMetadata>`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec<models::PriceFeedMetadata>`")))), } } else { let content = resp.text().await?; @@ -602,8 +602,8 @@ pub async fn price_stream_sse_handler(configuration: &configuration::Configurati let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), } } else { let content = resp.text().await?; @@ -656,8 +656,8 @@ pub async fn timestamp_price_updates(configuration: &configuration::Configuratio let content = resp.text().await?; match content_type { ContentType::Json => serde_json::from_str(&content).map_err(Error::from), - ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), - ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), + ContentType::Text => Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PriceUpdate`"))), + ContentType::Unsupported(unknown_type) => Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PriceUpdate`")))), } } else { let content = resp.text().await?; diff --git a/apps/hermes/client/rust/src/models/asset_type.rs b/apps/hermes/client/rust/src/models/asset_type.rs index fa49e213c0..be3ffb61f6 100644 --- a/apps/hermes/client/rust/src/models/asset_type.rs +++ b/apps/hermes/client/rust/src/models/asset_type.rs @@ -11,7 +11,6 @@ use crate::models; use serde::{Deserialize, Serialize}; -/// #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum AssetType { #[serde(rename = "crypto")] diff --git a/apps/hermes/client/rust/src/models/encoding_type.rs b/apps/hermes/client/rust/src/models/encoding_type.rs index edbbf12e61..7f85260edb 100644 --- a/apps/hermes/client/rust/src/models/encoding_type.rs +++ b/apps/hermes/client/rust/src/models/encoding_type.rs @@ -11,7 +11,6 @@ use crate::models; use serde::{Deserialize, Serialize}; -/// #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum EncodingType { #[serde(rename = "hex")] diff --git a/apps/hermes/client/rust/src/models/sse_event.rs b/apps/hermes/client/rust/src/models/sse_event.rs index a1adbd5290..69930cfc49 100644 --- a/apps/hermes/client/rust/src/models/sse_event.rs +++ b/apps/hermes/client/rust/src/models/sse_event.rs @@ -35,3 +35,9 @@ impl SseEvent { } } } + +impl Default for SseEvent { + fn default() -> Self { + Self::new() + } +} diff --git a/apps/hermes/client/rust/src/streaming.rs b/apps/hermes/client/rust/src/streaming.rs index 3b539749b3..868f783f71 100644 --- a/apps/hermes/client/rust/src/streaming.rs +++ b/apps/hermes/client/rust/src/streaming.rs @@ -72,7 +72,7 @@ pub async fn create_price_update_stream( Ok(sse_event) => { if let Some(parsed_updates) = sse_event.parsed { let stream = parsed_updates.into_iter() - .map(|update| Ok(update)) + .map(Ok) .collect::>>>(); Ok(futures_util::stream::iter(stream)) } else { @@ -88,10 +88,7 @@ pub async fn create_price_update_stream( .flat_map(|result| { match result { Ok(stream) => stream, - Err(e) => { - let err = e; - futures_util::stream::iter(vec![Err(err)]) - } + Err(e) => futures_util::stream::iter(vec![Err(e)]) } }); From ea933308c146e73d8c9cd0ef51489ded772d1482 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:19:38 +0000 Subject: [PATCH 10/12] fix: format code with cargo fmt Co-Authored-By: Tejas Badadare --- .../client/rust/examples/latest_prices.rs | 35 +-- .../client/rust/examples/price_stream.rs | 42 ++- .../client/rust/src/apis/configuration.rs | 5 +- apps/hermes/client/rust/src/apis/mod.rs | 18 +- apps/hermes/client/rust/src/apis/rest_api.rs | 293 +++++++++++++++--- apps/hermes/client/rust/src/lib.rs | 4 +- .../client/rust/src/models/asset_type.rs | 4 +- .../client/rust/src/models/binary_update.rs | 8 +- .../client/rust/src/models/encoding_type.rs | 4 +- .../rust/src/models/get_vaa_ccip_response.rs | 7 +- .../rust/src/models/get_vaa_response.rs | 8 +- ...blisher_stake_caps_update_data_response.rs | 10 +- .../rust/src/models/parsed_price_feed_twap.rs | 11 +- .../rust/src/models/parsed_price_update.rs | 10 +- .../src/models/parsed_publisher_stake_cap.rs | 8 +- .../parsed_publisher_stake_caps_update.rs | 7 +- .../rust/src/models/price_feed_metadata.rs | 13 +- .../client/rust/src/models/price_update.rs | 10 +- .../client/rust/src/models/rpc_price.rs | 3 +- .../client/rust/src/models/rpc_price_feed.rs | 17 +- .../src/models/rpc_price_feed_metadata.rs | 24 +- .../src/models/rpc_price_feed_metadata_v2.rs | 24 +- .../client/rust/src/models/sse_event.rs | 10 +- .../client/rust/src/models/twaps_response.rs | 10 +- apps/hermes/client/rust/src/streaming.rs | 43 +-- 25 files changed, 427 insertions(+), 201 deletions(-) diff --git a/apps/hermes/client/rust/examples/latest_prices.rs b/apps/hermes/client/rust/examples/latest_prices.rs index 5fca65a1db..aa06c3491f 100644 --- a/apps/hermes/client/rust/examples/latest_prices.rs +++ b/apps/hermes/client/rust/examples/latest_prices.rs @@ -10,23 +10,21 @@ const ETH_PRICE_FEED_ID: &str = "ff61491a931112ddf1bd8147cd1b641375f79f5825126d6 async fn main() -> Result<(), Box> { let mut config = Configuration::new(); config.base_path = "https://hermes.pyth.network".to_string(); - - let price_feed_ids = vec![ - BTC_PRICE_FEED_ID.to_string(), - ETH_PRICE_FEED_ID.to_string() - ]; - + + let price_feed_ids = vec![BTC_PRICE_FEED_ID.to_string(), ETH_PRICE_FEED_ID.to_string()]; + let price_update = rest_api::latest_price_updates( - &config, - price_feed_ids, - Some(EncodingType::Base64), + &config, + price_feed_ids, + Some(EncodingType::Base64), Some(true), // parsed - Some(false) // ignore_invalid_price_ids - ).await?; - + Some(false), // ignore_invalid_price_ids + ) + .await?; + println!("Latest Price Updates:"); println!("===================="); - + if let Some(Some(parsed_updates)) = price_update.parsed { for update in parsed_updates { let price_feed_id = update.id; @@ -35,22 +33,19 @@ async fn main() -> Result<(), Box> { ETH_PRICE_FEED_ID => "ETH/USD", _ => "Unknown", }; - + let price = &update.price; let price_value = price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); let conf_value = price.conf.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); - + println!( "{}: ${:.2} (conf: ${:.2}, publish_time: {})", - symbol, - price_value, - conf_value, - price.publish_time + symbol, price_value, conf_value, price.publish_time ); } } else { println!("No parsed price data available"); } - + Ok(()) } diff --git a/apps/hermes/client/rust/examples/price_stream.rs b/apps/hermes/client/rust/examples/price_stream.rs index cf15b772fe..69fb2cc68c 100644 --- a/apps/hermes/client/rust/examples/price_stream.rs +++ b/apps/hermes/client/rust/examples/price_stream.rs @@ -1,6 +1,6 @@ use futures_util::stream::StreamExt; -use hermes_client::Configuration; use hermes_client::create_price_update_stream; +use hermes_client::Configuration; use std::error::Error; const BTC_PRICE_FEED_ID: &str = "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"; @@ -10,25 +10,23 @@ const ETH_PRICE_FEED_ID: &str = "ff61491a931112ddf1bd8147cd1b641375f79f5825126d6 async fn main() -> Result<(), Box> { let mut config = Configuration::new(); config.base_path = "https://hermes.pyth.network".to_string(); - - let price_feed_ids = vec![ - BTC_PRICE_FEED_ID.to_string(), - ETH_PRICE_FEED_ID.to_string() - ]; - + + let price_feed_ids = vec![BTC_PRICE_FEED_ID.to_string(), ETH_PRICE_FEED_ID.to_string()]; + println!("Starting SSE price stream for BTC/USD and ETH/USD..."); println!("Press Ctrl+C to exit"); println!("===================="); - + let mut stream = create_price_update_stream( &config, price_feed_ids, - None, // default encoding (base64) - None, // default allow_unordered - None, // default benchmarks_only - None // default ignore_invalid_price_ids - ).await?; - + None, // default encoding (base64) + None, // default allow_unordered + None, // default benchmarks_only + None, // default ignore_invalid_price_ids + ) + .await?; + while let Some(result) = stream.next().await { match result { Ok(update) => { @@ -38,24 +36,22 @@ async fn main() -> Result<(), Box> { ETH_PRICE_FEED_ID => "ETH/USD", _ => "Unknown", }; - + let price = &update.price; - let price_value = price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); + let price_value = + price.price.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); let conf_value = price.conf.parse::().unwrap_or(0.0) * 10f64.powi(price.expo); - + println!( "{}: ${:.2} (conf: ${:.2}, publish_time: {})", - symbol, - price_value, - conf_value, - price.publish_time + symbol, price_value, conf_value, price.publish_time ); - }, + } Err(e) => { eprintln!("Error: {}", e); } } } - + Ok(()) } diff --git a/apps/hermes/client/rust/src/apis/configuration.rs b/apps/hermes/client/rust/src/apis/configuration.rs index 68aa0f2069..743605e423 100644 --- a/apps/hermes/client/rust/src/apis/configuration.rs +++ b/apps/hermes/client/rust/src/apis/configuration.rs @@ -4,12 +4,10 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ - - #[derive(Debug, Clone)] pub struct Configuration { pub base_path: String, @@ -29,7 +27,6 @@ pub struct ApiKey { pub key: String, } - impl Configuration { pub fn new() -> Configuration { Configuration::default() diff --git a/apps/hermes/client/rust/src/apis/mod.rs b/apps/hermes/client/rust/src/apis/mod.rs index cdffe1bf82..06d5976bc8 100644 --- a/apps/hermes/client/rust/src/apis/mod.rs +++ b/apps/hermes/client/rust/src/apis/mod.rs @@ -16,7 +16,7 @@ pub enum Error { ResponseError(ResponseContent), } -impl fmt::Display for Error { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (module, e) = match self { Error::Reqwest(e) => ("reqwest", e.to_string()), @@ -28,7 +28,7 @@ impl fmt::Display for Error { } } -impl error::Error for Error { +impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(match self { Error::Reqwest(e) => e, @@ -39,19 +39,19 @@ impl error::Error for Error { } } -impl From for Error { +impl From for Error { fn from(e: reqwest::Error) -> Self { Error::Reqwest(e) } } -impl From for Error { +impl From for Error { fn from(e: serde_json::Error) -> Self { Error::Serde(e) } } -impl From for Error { +impl From for Error { fn from(e: std::io::Error) -> Self { Error::Io(e) } @@ -78,8 +78,10 @@ pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String value, )); } - }, - serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), + } + serde_json::Value::String(s) => { + params.push((format!("{}[{}]", prefix, key), s.clone())) + } _ => params.push((format!("{}[{}]", prefix, key), value.to_string())), } } @@ -96,7 +98,7 @@ pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String enum ContentType { Json, Text, - Unsupported(String) + Unsupported(String), } impl From<&str> for ContentType { diff --git a/apps/hermes/client/rust/src/apis/rest_api.rs b/apps/hermes/client/rust/src/apis/rest_api.rs index b04b4be017..396058457b 100644 --- a/apps/hermes/client/rust/src/apis/rest_api.rs +++ b/apps/hermes/client/rust/src/apis/rest_api.rs @@ -4,16 +4,14 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ - -use reqwest; -use serde::{Deserialize, Serialize, de::Error as _}; +use super::{configuration, ContentType, Error}; use crate::{apis::ResponseContent, models}; -use super::{Error, configuration, ContentType}; - +use reqwest; +use serde::{de::Error as _, Deserialize, Serialize}; /// struct for typed errors of method [`get_price_feed`] #[derive(Debug, Clone, Serialize, Deserialize)] @@ -104,9 +102,14 @@ pub enum TimestampPriceUpdatesError { UnknownValue(serde_json::Value), } - /// **Deprecated: use /v2/updates/price/{publish_time} instead** Get a price update for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp. -pub async fn get_price_feed(configuration: &configuration::Configuration, id: &str, publish_time: i64, verbose: Option, binary: Option) -> Result> { +pub async fn get_price_feed( + configuration: &configuration::Configuration, + id: &str, + publish_time: i64, + verbose: Option, + binary: Option, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_id = id; let p_publish_time = publish_time; @@ -149,12 +152,20 @@ pub async fn get_price_feed(configuration: &configuration::Configuration, id: &s } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// **Deprecated: use /v2/updates/price/{publish_time} instead** Get a VAA for a price feed with a specific timestamp Given a price feed id and timestamp, retrieve the Pyth price update closest to that timestamp. -pub async fn get_vaa(configuration: &configuration::Configuration, id: &str, publish_time: i64) -> Result> { +pub async fn get_vaa( + configuration: &configuration::Configuration, + id: &str, + publish_time: i64, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_id = id; let p_publish_time = publish_time; @@ -189,12 +200,19 @@ pub async fn get_vaa(configuration: &configuration::Configuration, id: &str, pub } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// **Deprecated: use /v2/updates/price/{publish_time} instead** Get a VAA for a price feed using CCIP This endpoint accepts a single argument which is a hex-encoded byte string of the following form: ` ` -pub async fn get_vaa_ccip(configuration: &configuration::Configuration, data: std::path::PathBuf) -> Result> { +pub async fn get_vaa_ccip( + configuration: &configuration::Configuration, + data: std::path::PathBuf, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_data = data; @@ -227,12 +245,21 @@ pub async fn get_vaa_ccip(configuration: &configuration::Configuration, data: st } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// **Deprecated: use /v2/updates/price/latest instead** Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. -pub async fn latest_price_feeds(configuration: &configuration::Configuration, ids: Vec, verbose: Option, binary: Option) -> Result, Error> { +pub async fn latest_price_feeds( + configuration: &configuration::Configuration, + ids: Vec, + verbose: Option, + binary: Option, +) -> Result, Error> { // add a prefix to parameters to efficiently prevent name collisions let p_ids = ids; let p_verbose = verbose; @@ -242,8 +269,21 @@ pub async fn latest_price_feeds(configuration: &configuration::Configuration, id let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query( + &p_ids + .into_iter() + .map(|p| ("ids[]".to_owned(), p.to_string())) + .collect::>(), + ), + _ => req_builder.query(&[( + "ids[]", + &p_ids + .into_iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + .to_string(), + )]), }; if let Some(ref param_value) = p_verbose { req_builder = req_builder.query(&[("verbose", ¶m_value.to_string())]); @@ -276,12 +316,22 @@ pub async fn latest_price_feeds(configuration: &configuration::Configuration, id } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. -pub async fn latest_price_updates(configuration: &configuration::Configuration, ids: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn latest_price_updates( + configuration: &configuration::Configuration, + ids: Vec, + encoding: Option, + parsed: Option, + ignore_invalid_price_ids: Option, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_ids = ids; let p_encoding = encoding; @@ -292,8 +342,21 @@ pub async fn latest_price_updates(configuration: &configuration::Configuration, let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query( + &p_ids + .into_iter() + .map(|p| ("ids[]".to_owned(), p.to_string())) + .collect::>(), + ), + _ => req_builder.query(&[( + "ids[]", + &p_ids + .into_iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + .to_string(), + )]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -329,17 +392,29 @@ pub async fn latest_price_updates(configuration: &configuration::Configuration, } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// Get the most recent publisher stake caps update data. -pub async fn latest_publisher_stake_caps(configuration: &configuration::Configuration, encoding: Option, parsed: Option) -> Result> { +pub async fn latest_publisher_stake_caps( + configuration: &configuration::Configuration, + encoding: Option, + parsed: Option, +) -> Result> +{ // add a prefix to parameters to efficiently prevent name collisions let p_encoding = encoding; let p_parsed = parsed; - let uri_str = format!("{}/v2/updates/publisher_stake_caps/latest", configuration.base_path); + let uri_str = format!( + "{}/v2/updates/publisher_stake_caps/latest", + configuration.base_path + ); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); if let Some(ref param_value) = p_encoding { @@ -373,25 +448,54 @@ pub async fn latest_publisher_stake_caps(configuration: &configuration::Configur } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// Get the latest TWAP by price feed id with a custom time window. Given a collection of price feed ids, retrieve the latest Pyth TWAP price for each price feed. -pub async fn latest_twaps(configuration: &configuration::Configuration, window_seconds: i64, ids_left_square_bracket_right_square_bracket: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn latest_twaps( + configuration: &configuration::Configuration, + window_seconds: i64, + ids_left_square_bracket_right_square_bracket: Vec, + encoding: Option, + parsed: Option, + ignore_invalid_price_ids: Option, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_window_seconds = window_seconds; - let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_ids_left_square_bracket_right_square_bracket = + ids_left_square_bracket_right_square_bracket; let p_encoding = encoding; let p_parsed = parsed; let p_ignore_invalid_price_ids = ignore_invalid_price_ids; - let uri_str = format!("{}/v2/updates/twap/{window_seconds}/latest", configuration.base_path, window_seconds=p_window_seconds); + let uri_str = format!( + "{}/v2/updates/twap/{window_seconds}/latest", + configuration.base_path, + window_seconds = p_window_seconds + ); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query( + &p_ids_left_square_bracket_right_square_bracket + .into_iter() + .map(|p| ("ids[]".to_owned(), p.to_string())) + .collect::>(), + ), + _ => req_builder.query(&[( + "ids[]", + &p_ids_left_square_bracket_right_square_bracket + .into_iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + .to_string(), + )]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -427,21 +531,42 @@ pub async fn latest_twaps(configuration: &configuration::Configuration, window_s } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// **Deprecated: use /v2/updates/price/latest instead** Get VAAs for a set of price feed ids. Given a collection of price feed ids, retrieve the latest VAA for each. The returned VAA(s) can be submitted to the Pyth contract to update the on-chain price. If VAAs are not found for every provided price ID the call will fail. -pub async fn latest_vaas(configuration: &configuration::Configuration, ids_left_square_bracket_right_square_bracket: Vec) -> Result, Error> { +pub async fn latest_vaas( + configuration: &configuration::Configuration, + ids_left_square_bracket_right_square_bracket: Vec, +) -> Result, Error> { // add a prefix to parameters to efficiently prevent name collisions - let p_ids_left_square_bracket_right_square_bracket = ids_left_square_bracket_right_square_bracket; + let p_ids_left_square_bracket_right_square_bracket = + ids_left_square_bracket_right_square_bracket; let uri_str = format!("{}/api/latest_vaas", configuration.base_path); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids_left_square_bracket_right_square_bracket.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query( + &p_ids_left_square_bracket_right_square_bracket + .into_iter() + .map(|p| ("ids[]".to_owned(), p.to_string())) + .collect::>(), + ), + _ => req_builder.query(&[( + "ids[]", + &p_ids_left_square_bracket_right_square_bracket + .into_iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + .to_string(), + )]), }; if let Some(ref user_agent) = configuration.user_agent { req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); @@ -468,13 +593,18 @@ pub async fn latest_vaas(configuration: &configuration::Configuration, ids_left_ } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// **Deprecated: use /v2/price_feeds instead** Get the set of price feed IDs. This endpoint fetches all of the price feed IDs for which price updates can be retrieved. -pub async fn price_feed_ids(configuration: &configuration::Configuration, ) -> Result, Error> { - +pub async fn price_feed_ids( + configuration: &configuration::Configuration, +) -> Result, Error> { let uri_str = format!("{}/api/price_feed_ids", configuration.base_path); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); @@ -503,12 +633,20 @@ pub async fn price_feed_ids(configuration: &configuration::Configuration, ) -> R } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// Get the set of price feeds. This endpoint fetches all price feeds from the Pyth network. It can be filtered by asset type and query string. -pub async fn price_feeds_metadata(configuration: &configuration::Configuration, query: Option<&str>, asset_type: Option) -> Result, Error> { +pub async fn price_feeds_metadata( + configuration: &configuration::Configuration, + query: Option<&str>, + asset_type: Option, +) -> Result, Error> { // add a prefix to parameters to efficiently prevent name collisions let p_query = query; let p_asset_type = asset_type; @@ -547,12 +685,24 @@ pub async fn price_feeds_metadata(configuration: &configuration::Configuration, } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// SSE route handler for streaming price updates. The connection will automatically close after 24 hours to prevent resource leaks. Clients should implement reconnection logic to maintain continuous price updates. -pub async fn price_stream_sse_handler(configuration: &configuration::Configuration, ids: Vec, encoding: Option, parsed: Option, allow_unordered: Option, benchmarks_only: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn price_stream_sse_handler( + configuration: &configuration::Configuration, + ids: Vec, + encoding: Option, + parsed: Option, + allow_unordered: Option, + benchmarks_only: Option, + ignore_invalid_price_ids: Option, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_ids = ids; let p_encoding = encoding; @@ -565,8 +715,21 @@ pub async fn price_stream_sse_handler(configuration: &configuration::Configurati let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query( + &p_ids + .into_iter() + .map(|p| ("ids[]".to_owned(), p.to_string())) + .collect::>(), + ), + _ => req_builder.query(&[( + "ids[]", + &p_ids + .into_iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + .to_string(), + )]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -608,12 +771,23 @@ pub async fn price_stream_sse_handler(configuration: &configuration::Configurati } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } /// Get the latest price updates by price feed id. Given a collection of price feed ids, retrieve the latest Pyth price for each price feed. -pub async fn timestamp_price_updates(configuration: &configuration::Configuration, publish_time: i64, ids: Vec, encoding: Option, parsed: Option, ignore_invalid_price_ids: Option) -> Result> { +pub async fn timestamp_price_updates( + configuration: &configuration::Configuration, + publish_time: i64, + ids: Vec, + encoding: Option, + parsed: Option, + ignore_invalid_price_ids: Option, +) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_publish_time = publish_time; let p_ids = ids; @@ -621,12 +795,29 @@ pub async fn timestamp_price_updates(configuration: &configuration::Configuratio let p_parsed = parsed; let p_ignore_invalid_price_ids = ignore_invalid_price_ids; - let uri_str = format!("{}/v2/updates/price/{publish_time}", configuration.base_path, publish_time=p_publish_time); + let uri_str = format!( + "{}/v2/updates/price/{publish_time}", + configuration.base_path, + publish_time = p_publish_time + ); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); req_builder = match "multi" { - "multi" => req_builder.query(&p_ids.into_iter().map(|p| ("ids[]".to_owned(), p.to_string())).collect::>()), - _ => req_builder.query(&[("ids[]", &p_ids.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + "multi" => req_builder.query( + &p_ids + .into_iter() + .map(|p| ("ids[]".to_owned(), p.to_string())) + .collect::>(), + ), + _ => req_builder.query(&[( + "ids[]", + &p_ids + .into_iter() + .map(|p| p.to_string()) + .collect::>() + .join(",") + .to_string(), + )]), }; if let Some(ref param_value) = p_encoding { req_builder = req_builder.query(&[("encoding", ¶m_value.to_string())]); @@ -662,6 +853,10 @@ pub async fn timestamp_price_updates(configuration: &configuration::Configuratio } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { + status, + content, + entity, + })) } } diff --git a/apps/hermes/client/rust/src/lib.rs b/apps/hermes/client/rust/src/lib.rs index 824ded5045..820a07bccc 100644 --- a/apps/hermes/client/rust/src/lib.rs +++ b/apps/hermes/client/rust/src/lib.rs @@ -1,11 +1,11 @@ #![allow(unused_imports)] #![allow(clippy::too_many_arguments)] -extern crate serde_repr; +extern crate reqwest; extern crate serde; extern crate serde_json; +extern crate serde_repr; extern crate url; -extern crate reqwest; pub mod apis; pub mod models; diff --git a/apps/hermes/client/rust/src/models/asset_type.rs b/apps/hermes/client/rust/src/models/asset_type.rs index be3ffb61f6..16d0a33540 100644 --- a/apps/hermes/client/rust/src/models/asset_type.rs +++ b/apps/hermes/client/rust/src/models/asset_type.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -25,7 +25,6 @@ pub enum AssetType { Rates, #[serde(rename = "crypto_redemption_rate")] CryptoRedemptionRate, - } impl std::fmt::Display for AssetType { @@ -46,4 +45,3 @@ impl Default for AssetType { Self::Crypto } } - diff --git a/apps/hermes/client/rust/src/models/binary_update.rs b/apps/hermes/client/rust/src/models/binary_update.rs index 57bf2cfd2a..6d51a343f2 100644 --- a/apps/hermes/client/rust/src/models/binary_update.rs +++ b/apps/hermes/client/rust/src/models/binary_update.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -21,10 +21,6 @@ pub struct BinaryUpdate { impl BinaryUpdate { pub fn new(data: Vec, encoding: models::EncodingType) -> BinaryUpdate { - BinaryUpdate { - data, - encoding, - } + BinaryUpdate { data, encoding } } } - diff --git a/apps/hermes/client/rust/src/models/encoding_type.rs b/apps/hermes/client/rust/src/models/encoding_type.rs index 7f85260edb..e91b27cb52 100644 --- a/apps/hermes/client/rust/src/models/encoding_type.rs +++ b/apps/hermes/client/rust/src/models/encoding_type.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -17,7 +17,6 @@ pub enum EncodingType { Hex, #[serde(rename = "base64")] Base64, - } impl std::fmt::Display for EncodingType { @@ -34,4 +33,3 @@ impl Default for EncodingType { Self::Hex } } - diff --git a/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs b/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs index e009a6a3aa..e1b147fd2c 100644 --- a/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs +++ b/apps/hermes/client/rust/src/models/get_vaa_ccip_response.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -19,9 +19,6 @@ pub struct GetVaaCcipResponse { impl GetVaaCcipResponse { pub fn new(data: String) -> GetVaaCcipResponse { - GetVaaCcipResponse { - data, - } + GetVaaCcipResponse { data } } } - diff --git a/apps/hermes/client/rust/src/models/get_vaa_response.rs b/apps/hermes/client/rust/src/models/get_vaa_response.rs index 2f587f9384..d15ecf93bb 100644 --- a/apps/hermes/client/rust/src/models/get_vaa_response.rs +++ b/apps/hermes/client/rust/src/models/get_vaa_response.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -22,10 +22,6 @@ pub struct GetVaaResponse { impl GetVaaResponse { pub fn new(publish_time: i64, vaa: String) -> GetVaaResponse { - GetVaaResponse { - publish_time, - vaa, - } + GetVaaResponse { publish_time, vaa } } } - diff --git a/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs b/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs index c47cca23b8..b04d0234d1 100644 --- a/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs +++ b/apps/hermes/client/rust/src/models/latest_publisher_stake_caps_update_data_response.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -15,7 +15,12 @@ use serde::{Deserialize, Serialize}; pub struct LatestPublisherStakeCapsUpdateDataResponse { #[serde(rename = "binary")] pub binary: Box, - #[serde(rename = "parsed", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "parsed", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub parsed: Option>>, } @@ -27,4 +32,3 @@ impl LatestPublisherStakeCapsUpdateDataResponse { } } } - diff --git a/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs b/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs index 120776b31c..daa946bb69 100644 --- a/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs +++ b/apps/hermes/client/rust/src/models/parsed_price_feed_twap.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -29,7 +29,13 @@ pub struct ParsedPriceFeedTwap { } impl ParsedPriceFeedTwap { - pub fn new(down_slots_ratio: String, end_timestamp: i64, id: String, start_timestamp: i64, twap: models::RpcPrice) -> ParsedPriceFeedTwap { + pub fn new( + down_slots_ratio: String, + end_timestamp: i64, + id: String, + start_timestamp: i64, + twap: models::RpcPrice, + ) -> ParsedPriceFeedTwap { ParsedPriceFeedTwap { down_slots_ratio, end_timestamp, @@ -39,4 +45,3 @@ impl ParsedPriceFeedTwap { } } } - diff --git a/apps/hermes/client/rust/src/models/parsed_price_update.rs b/apps/hermes/client/rust/src/models/parsed_price_update.rs index 3f557c9f5a..b503de7d69 100644 --- a/apps/hermes/client/rust/src/models/parsed_price_update.rs +++ b/apps/hermes/client/rust/src/models/parsed_price_update.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -24,7 +24,12 @@ pub struct ParsedPriceUpdate { } impl ParsedPriceUpdate { - pub fn new(ema_price: models::RpcPrice, id: String, metadata: models::RpcPriceFeedMetadataV2, price: models::RpcPrice) -> ParsedPriceUpdate { + pub fn new( + ema_price: models::RpcPrice, + id: String, + metadata: models::RpcPriceFeedMetadataV2, + price: models::RpcPrice, + ) -> ParsedPriceUpdate { ParsedPriceUpdate { ema_price: Box::new(ema_price), id, @@ -33,4 +38,3 @@ impl ParsedPriceUpdate { } } } - diff --git a/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs b/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs index cec953eb95..46e6cbf7e7 100644 --- a/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs +++ b/apps/hermes/client/rust/src/models/parsed_publisher_stake_cap.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -21,10 +21,6 @@ pub struct ParsedPublisherStakeCap { impl ParsedPublisherStakeCap { pub fn new(cap: i64, publisher: String) -> ParsedPublisherStakeCap { - ParsedPublisherStakeCap { - cap, - publisher, - } + ParsedPublisherStakeCap { cap, publisher } } } - diff --git a/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs b/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs index f140b388bf..5d91f640e9 100644 --- a/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs +++ b/apps/hermes/client/rust/src/models/parsed_publisher_stake_caps_update.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -18,10 +18,11 @@ pub struct ParsedPublisherStakeCapsUpdate { } impl ParsedPublisherStakeCapsUpdate { - pub fn new(publisher_stake_caps: Vec) -> ParsedPublisherStakeCapsUpdate { + pub fn new( + publisher_stake_caps: Vec, + ) -> ParsedPublisherStakeCapsUpdate { ParsedPublisherStakeCapsUpdate { publisher_stake_caps, } } } - diff --git a/apps/hermes/client/rust/src/models/price_feed_metadata.rs b/apps/hermes/client/rust/src/models/price_feed_metadata.rs index 936b624edd..42a1f55f11 100644 --- a/apps/hermes/client/rust/src/models/price_feed_metadata.rs +++ b/apps/hermes/client/rust/src/models/price_feed_metadata.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -20,11 +20,10 @@ pub struct PriceFeedMetadata { } impl PriceFeedMetadata { - pub fn new(attributes: std::collections::HashMap, id: String) -> PriceFeedMetadata { - PriceFeedMetadata { - attributes, - id, - } + pub fn new( + attributes: std::collections::HashMap, + id: String, + ) -> PriceFeedMetadata { + PriceFeedMetadata { attributes, id } } } - diff --git a/apps/hermes/client/rust/src/models/price_update.rs b/apps/hermes/client/rust/src/models/price_update.rs index f407fee60d..47058a5f0a 100644 --- a/apps/hermes/client/rust/src/models/price_update.rs +++ b/apps/hermes/client/rust/src/models/price_update.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -15,7 +15,12 @@ use serde::{Deserialize, Serialize}; pub struct PriceUpdate { #[serde(rename = "binary")] pub binary: Box, - #[serde(rename = "parsed", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "parsed", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub parsed: Option>>, } @@ -27,4 +32,3 @@ impl PriceUpdate { } } } - diff --git a/apps/hermes/client/rust/src/models/rpc_price.rs b/apps/hermes/client/rust/src/models/rpc_price.rs index 41067e7a1e..cd37b85210 100644 --- a/apps/hermes/client/rust/src/models/rpc_price.rs +++ b/apps/hermes/client/rust/src/models/rpc_price.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -39,4 +39,3 @@ impl RpcPrice { } } } - diff --git a/apps/hermes/client/rust/src/models/rpc_price_feed.rs b/apps/hermes/client/rust/src/models/rpc_price_feed.rs index f877794d94..ecba7300f3 100644 --- a/apps/hermes/client/rust/src/models/rpc_price_feed.rs +++ b/apps/hermes/client/rust/src/models/rpc_price_feed.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -17,12 +17,22 @@ pub struct RpcPriceFeed { pub ema_price: Box, #[serde(rename = "id")] pub id: String, - #[serde(rename = "metadata", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "metadata", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub metadata: Option>>, #[serde(rename = "price")] pub price: Box, /// The VAA binary represented as a base64 string. - #[serde(rename = "vaa", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "vaa", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub vaa: Option>, } @@ -37,4 +47,3 @@ impl RpcPriceFeed { } } } - diff --git a/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs index 1fe72c1975..d24151e475 100644 --- a/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs +++ b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -15,11 +15,26 @@ use serde::{Deserialize, Serialize}; pub struct RpcPriceFeedMetadata { #[serde(rename = "emitter_chain")] pub emitter_chain: i32, - #[serde(rename = "prev_publish_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "prev_publish_time", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub prev_publish_time: Option>, - #[serde(rename = "price_service_receive_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "price_service_receive_time", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub price_service_receive_time: Option>, - #[serde(rename = "slot", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "slot", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub slot: Option>, } @@ -33,4 +48,3 @@ impl RpcPriceFeedMetadata { } } } - diff --git a/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs index cc3296b7b2..5376da7057 100644 --- a/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs +++ b/apps/hermes/client/rust/src/models/rpc_price_feed_metadata_v2.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -13,11 +13,26 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct RpcPriceFeedMetadataV2 { - #[serde(rename = "prev_publish_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "prev_publish_time", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub prev_publish_time: Option>, - #[serde(rename = "proof_available_time", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "proof_available_time", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub proof_available_time: Option>, - #[serde(rename = "slot", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "slot", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub slot: Option>, } @@ -30,4 +45,3 @@ impl RpcPriceFeedMetadataV2 { } } } - diff --git a/apps/hermes/client/rust/src/models/sse_event.rs b/apps/hermes/client/rust/src/models/sse_event.rs index 69930cfc49..779bd480c4 100644 --- a/apps/hermes/client/rust/src/models/sse_event.rs +++ b/apps/hermes/client/rust/src/models/sse_event.rs @@ -1,15 +1,15 @@ +use super::parsed_price_update::ParsedPriceUpdate; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use super::parsed_price_update::ParsedPriceUpdate; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BinaryData { #[serde(rename = "encoding")] pub encoding: String, - + #[serde(rename = "data")] pub data: Vec, - + #[serde(flatten)] pub additional_properties: HashMap, } @@ -18,10 +18,10 @@ pub struct BinaryData { pub struct SseEvent { #[serde(rename = "binary", skip_serializing_if = "Option::is_none")] pub binary: Option, - + #[serde(rename = "parsed", skip_serializing_if = "Option::is_none")] pub parsed: Option>, - + #[serde(flatten)] pub additional_properties: HashMap, } diff --git a/apps/hermes/client/rust/src/models/twaps_response.rs b/apps/hermes/client/rust/src/models/twaps_response.rs index 64b074fcd6..79ebc0f353 100644 --- a/apps/hermes/client/rust/src/models/twaps_response.rs +++ b/apps/hermes/client/rust/src/models/twaps_response.rs @@ -4,7 +4,7 @@ * Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle. * * The version of the OpenAPI document: 0.8.6 - * + * * Generated by: https://openapi-generator.tech */ @@ -16,7 +16,12 @@ pub struct TwapsResponse { #[serde(rename = "binary")] pub binary: Box, /// The calculated TWAPs for each price ID - #[serde(rename = "parsed", default, with = "::serde_with::rust::double_option", skip_serializing_if = "Option::is_none")] + #[serde( + rename = "parsed", + default, + with = "::serde_with::rust::double_option", + skip_serializing_if = "Option::is_none" + )] pub parsed: Option>>, } @@ -28,4 +33,3 @@ impl TwapsResponse { } } } - diff --git a/apps/hermes/client/rust/src/streaming.rs b/apps/hermes/client/rust/src/streaming.rs index 868f783f71..4c1d44b4c0 100644 --- a/apps/hermes/client/rust/src/streaming.rs +++ b/apps/hermes/client/rust/src/streaming.rs @@ -1,4 +1,3 @@ - use crate::apis::configuration::Configuration; use crate::models::{EncodingType, ParsedPriceUpdate, PriceUpdate}; use futures_util::stream::{Stream, StreamExt}; @@ -11,7 +10,10 @@ pub async fn create_price_update_stream( allow_unordered: Option, benchmarks_only: Option, ignore_invalid_price_ids: Option, -) -> Result>>, Box> { +) -> Result< + impl Stream>>, + Box, +> { let base_url = format!("{}/v2/updates/price/stream", config.base_path); let mut url = reqwest::Url::parse(&base_url)?; @@ -42,10 +44,10 @@ pub async fn create_price_update_stream( drop(query_pairs); - let client = reqwest::Client::builder() - .build()?; + let client = reqwest::Client::builder().build()?; - let response = client.get(url) + let response = client + .get(url) .header("Accept", "text/event-stream") .send() .await?; @@ -56,9 +58,9 @@ pub async fn create_price_update_stream( let stream = response.bytes_stream(); - let sse_stream = eventsource_stream::EventStream::new(stream) - .map(move |event_result| { - match event_result { + let sse_stream = + eventsource_stream::EventStream::new(stream) + .map(move |event_result| match event_result { Ok(event) => { if event.event != "message" { return Err(format!("Unexpected event type: {}", event.event).into()); @@ -71,26 +73,27 @@ pub async fn create_price_update_stream( match serde_json::from_str::(data) { Ok(sse_event) => { if let Some(parsed_updates) = sse_event.parsed { - let stream = parsed_updates.into_iter() - .map(Ok) - .collect::>>>(); + let stream = + parsed_updates + .into_iter() + .map(Ok) + .collect::>, + >>(); Ok(futures_util::stream::iter(stream)) } else { Err("No parsed price updates in the response".into()) } - }, + } Err(e) => Err(format!("Failed to parse price update: {}", e).into()), } - }, + } Err(e) => Err(format!("Error in SSE stream: {}", e).into()), - } - }) - .flat_map(|result| { - match result { + }) + .flat_map(|result| match result { Ok(stream) => stream, - Err(e) => futures_util::stream::iter(vec![Err(e)]) - } - }); + Err(e) => futures_util::stream::iter(vec![Err(e)]), + }); Ok(sse_stream) } From 75f8be854395764fa023f99caf2d743e033cf006 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:23:18 +0000 Subject: [PATCH 11/12] fix: resolve formatting issues in streaming.rs Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/src/streaming.rs | 47 ++++++++++++------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/apps/hermes/client/rust/src/streaming.rs b/apps/hermes/client/rust/src/streaming.rs index 4c1d44b4c0..e5898638ff 100644 --- a/apps/hermes/client/rust/src/streaming.rs +++ b/apps/hermes/client/rust/src/streaming.rs @@ -58,42 +58,41 @@ pub async fn create_price_update_stream( let stream = response.bytes_stream(); - let sse_stream = - eventsource_stream::EventStream::new(stream) - .map(move |event_result| match event_result { - Ok(event) => { - if event.event != "message" { - return Err(format!("Unexpected event type: {}", event.event).into()); - } + let sse_stream = eventsource_stream::EventStream::new(stream) + .map(move |event_result| match event_result { + Ok(event) => { + if event.event != "message" { + return Err(format!("Unexpected event type: {}", event.event).into()); + } - let data = &event.data; + let data = &event.data; - println!("Received SSE data: {}", data); + println!("Received SSE data: {}", data); - match serde_json::from_str::(data) { - Ok(sse_event) => { - if let Some(parsed_updates) = sse_event.parsed { - let stream = + match serde_json::from_str::(data) { + Ok(sse_event) => { + if let Some(parsed_updates) = sse_event.parsed { + let stream = parsed_updates .into_iter() .map(Ok) .collect::>, >>(); - Ok(futures_util::stream::iter(stream)) - } else { - Err("No parsed price updates in the response".into()) - } + Ok(futures_util::stream::iter(stream)) + } else { + Err("No parsed price updates in the response".into()) } - Err(e) => Err(format!("Failed to parse price update: {}", e).into()), } + Err(e) => Err(format!("Failed to parse price update: {}", e).into()), } - Err(e) => Err(format!("Error in SSE stream: {}", e).into()), - }) - .flat_map(|result| match result { - Ok(stream) => stream, - Err(e) => futures_util::stream::iter(vec![Err(e)]), - }); + } + Err(e) => Err(format!("Error in SSE stream: {}", e).into()), + }) + .flat_map(|result| match result { + Ok(stream) => stream, + Err(e) => futures_util::stream::iter(vec![Err(e)]), + }); Ok(sse_stream) } From bdc0aacfaea46320479fd8f05668b79b3f854431 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:30:05 +0000 Subject: [PATCH 12/12] docs: add detailed regeneration steps to README Co-Authored-By: Tejas Badadare --- apps/hermes/client/rust/README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/hermes/client/rust/README.md b/apps/hermes/client/rust/README.md index dcc64e2a31..6d79381268 100644 --- a/apps/hermes/client/rust/README.md +++ b/apps/hermes/client/rust/README.md @@ -98,6 +98,22 @@ To regenerate the client after updates to the Hermes API: 3. Manually update dependencies in Cargo.toml and version number if necessary. +4. Create a `.openapi-generator-ignore` file to preserve custom implementations: + ```bash + echo "src/streaming.rs" > .openapi-generator-ignore + echo "examples/" >> .openapi-generator-ignore + ``` + +5. Implement custom streaming functionality in `src/streaming.rs` using the `eventsource-stream` crate for proper SSE handling. + +6. Rename parameter names in the generated code to be more intuitive (e.g., in `rest_api.rs`). + +7. Fix formatting and clippy linting issues: + ```bash + cargo fmt + cargo clippy --fix + ``` + ## Publishing This package is published to crates.io when a new tag matching `hermes-client-rust-v*` is pushed. @@ -125,6 +141,3 @@ The client defaults to using the Hermes public endpoint at https://hermes.pyth.n - [RpcPriceFeedMetadata](docs/RpcPriceFeedMetadata.md) - [RpcPriceFeedMetadataV2](docs/RpcPriceFeedMetadataV2.md) - [TwapsResponse](docs/TwapsResponse.md) - - -