Ver código fonte

add fuel contract by Fuel Labs (#1513)

Daniel Chew 1 ano atrás
pai
commit
c2da454637
56 arquivos alterados com 8807 adições e 0 exclusões
  1. 1 0
      target_chains/fuel/contracts/.gitignore
  2. 4229 0
      target_chains/fuel/contracts/Cargo.lock
  3. 27 0
      target_chains/fuel/contracts/Cargo.toml
  4. 44 0
      target_chains/fuel/contracts/Forc.lock
  5. 2 0
      target_chains/fuel/contracts/Forc.toml
  6. 201 0
      target_chains/fuel/contracts/LICENSE
  7. 44 0
      target_chains/fuel/contracts/README.md
  8. 6 0
      target_chains/fuel/contracts/fuel-toolchain.toml
  9. 10 0
      target_chains/fuel/contracts/pyth-contract/Forc.toml
  10. 8 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures.sw
  11. 133 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/accumulator_update.sw
  12. 95 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/batch_attestation_update.sw
  13. 31 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw
  14. 311 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/price.sw
  15. 37 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/update_type.sw
  16. 590 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw
  17. 63 0
      target_chains/fuel/contracts/pyth-contract/src/errors.sw
  18. 21 0
      target_chains/fuel/contracts/pyth-contract/src/events.sw
  19. 617 0
      target_chains/fuel/contracts/pyth-contract/src/main.sw
  20. 64 0
      target_chains/fuel/contracts/pyth-contract/src/pyth_merkle_proof.sw
  21. 21 0
      target_chains/fuel/contracts/pyth-contract/src/utils.sw
  22. 8 0
      target_chains/fuel/contracts/pyth-interface/Forc.toml
  23. 5 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures.sw
  24. 6 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures/data_source.sw
  25. 35 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures/price.sw
  26. 11 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw
  27. 308 0
      target_chains/fuel/contracts/pyth-interface/src/interface.sw
  28. 63 0
      target_chains/fuel/contracts/scripts/deploy_pyth.rs
  29. 11 0
      target_chains/fuel/contracts/src/constants.rs
  30. 2 0
      target_chains/fuel/contracts/src/lib.rs
  31. 233 0
      target_chains/fuel/contracts/src/pyth_utils.rs
  32. 4 0
      target_chains/fuel/contracts/tests/functions/mod.rs
  33. 102 0
      target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs
  34. 118 0
      target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs
  35. 102 0
      target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs
  36. 10 0
      target_chains/fuel/contracts/tests/functions/pyth_core/mod.rs
  37. 87 0
      target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs
  38. 102 0
      target_chains/fuel/contracts/tests/functions/pyth_core/price.rs
  39. 119 0
      target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs
  40. 101 0
      target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs
  41. 53 0
      target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs
  42. 115 0
      target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs
  43. 123 0
      target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs
  44. 1 0
      target_chains/fuel/contracts/tests/functions/pyth_info/mod.rs
  45. 88 0
      target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs
  46. 119 0
      target_chains/fuel/contracts/tests/functions/pyth_init/constuctor.rs
  47. 1 0
      target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs
  48. 0 0
      target_chains/fuel/contracts/tests/functions/wormhole_guardians/mod.rs
  49. 2 0
      target_chains/fuel/contracts/tests/harness.rs
  50. 4 0
      target_chains/fuel/contracts/tests/utils/interface/mod.rs
  51. 156 0
      target_chains/fuel/contracts/tests/utils/interface/pyth_core.rs
  52. 64 0
      target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs
  53. 25 0
      target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs
  54. 45 0
      target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs
  55. 2 0
      target_chains/fuel/contracts/tests/utils/mod.rs
  56. 27 0
      target_chains/fuel/contracts/tests/utils/setup.rs

+ 1 - 0
target_chains/fuel/contracts/.gitignore

@@ -0,0 +1 @@
+out/

+ 4229 - 0
target_chains/fuel/contracts/Cargo.lock

@@ -0,0 +1,4229 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "Inflector"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+
+[[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 = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
+
+[[package]]
+name = "ascii"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
+
+[[package]]
+name = "async-graphql"
+version = "4.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9ed522678d412d77effe47b3c82314ac36952a35e6e852093dd48287c421f80"
+dependencies = [
+ "async-graphql-derive",
+ "async-graphql-parser",
+ "async-graphql-value",
+ "async-stream",
+ "async-trait",
+ "base64 0.13.1",
+ "bytes",
+ "fnv",
+ "futures-util",
+ "http",
+ "indexmap 1.9.3",
+ "mime",
+ "multer",
+ "num-traits",
+ "once_cell",
+ "pin-project-lite",
+ "regex",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "static_assertions",
+ "tempfile",
+ "thiserror",
+ "tracing",
+ "tracing-futures",
+]
+
+[[package]]
+name = "async-graphql-derive"
+version = "4.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c121a894495d7d3fc3d4e15e0a9843e422e4d1d9e3c514d8062a1c94b35b005d"
+dependencies = [
+ "Inflector",
+ "async-graphql-parser",
+ "darling 0.14.4",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "thiserror",
+]
+
+[[package]]
+name = "async-graphql-parser"
+version = "4.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b6c386f398145c6180206c1869c2279f5a3d45db5be4e0266148c6ac5c6ad68"
+dependencies = [
+ "async-graphql-value",
+ "pest",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "async-graphql-value"
+version = "4.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a941b499fead4a3fb5392cabf42446566d18c86313f69f2deab69560394d65f"
+dependencies = [
+ "bytes",
+ "indexmap 1.9.3",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "atomic-polyfill"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
+dependencies = [
+ "critical-section",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "axum"
+version = "0.5.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bitflags 1.3.2",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-http",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "mime",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "serde",
+]
+
+[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bech32"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitvec"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "sha2",
+ "tinyvec",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b"
+
+[[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.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim 0.11.1",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "cobs"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
+
+[[package]]
+name = "coins-bip32"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3"
+dependencies = [
+ "bs58",
+ "coins-core",
+ "digest",
+ "hmac",
+ "k256",
+ "serde",
+ "sha2",
+ "thiserror",
+]
+
+[[package]]
+name = "coins-bip39"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528"
+dependencies = [
+ "bitvec",
+ "coins-bip32",
+ "hmac",
+ "once_cell",
+ "pbkdf2 0.12.2",
+ "rand",
+ "sha2",
+ "thiserror",
+]
+
+[[package]]
+name = "coins-core"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979"
+dependencies = [
+ "base64 0.21.7",
+ "bech32",
+ "bs58",
+ "digest",
+ "generic-array",
+ "hex",
+ "ripemd",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "sha3",
+ "thiserror",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "combine"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680"
+dependencies = [
+ "ascii",
+ "byteorder",
+ "either",
+ "memchr",
+ "unreachable",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "cookie"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "cookie_store"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6"
+dependencies = [
+ "cookie",
+ "idna 0.3.0",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "counter"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d458e66999348f56fd3ffcfbb7f7951542075ca8359687c703de6500c1ddccd"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "critical-section"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "crypto-bigint"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ct-logs"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8"
+dependencies = [
+ "sct 0.6.1",
+]
+
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "cynic"
+version = "2.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1afa0591b1021e427e548a1f0f147fe6168f6c7c7f7006bace77f28856051b8"
+dependencies = [
+ "cynic-proc-macros",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "static_assertions",
+ "thiserror",
+]
+
+[[package]]
+name = "cynic-codegen"
+version = "2.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a1bb05cc554f46079d0fa72abe995a2d32d0737d410a41da75b31e3f7ef768"
+dependencies = [
+ "counter",
+ "darling 0.13.4",
+ "graphql-parser",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "cynic-proc-macros"
+version = "2.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa595c4ed7a5374e0e58c5c34f9d93bd6b7d45062790963bd4b4c3c0bf520c4d"
+dependencies = [
+ "cynic-codegen",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
+dependencies = [
+ "darling_core 0.13.4",
+ "darling_macro 0.13.4",
+]
+
+[[package]]
+name = "darling"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
+dependencies = [
+ "darling_core 0.14.4",
+ "darling_macro 0.14.4",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
+dependencies = [
+ "darling_core 0.13.4",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+dependencies = [
+ "darling_core 0.14.4",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "der"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "const-oid",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dotenv"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
+
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
+[[package]]
+name = "ecdsa"
+version = "0.16.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+dependencies = [
+ "der",
+ "digest",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+ "spki",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "sha2",
+ "subtle",
+]
+
+[[package]]
+name = "either"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
+
+[[package]]
+name = "elliptic-curve"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "digest",
+ "ff",
+ "generic-array",
+ "group",
+ "pkcs8",
+ "rand_core",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "embedded-io"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "enum-iterator"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94"
+dependencies = [
+ "enum-iterator-derive",
+]
+
+[[package]]
+name = "enum-iterator-derive"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c19cbb53d33b57ac4df1f0af6b92c38c107cded663c4aea9fae1189dcfc17cf5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "eth-keystore"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab"
+dependencies = [
+ "aes",
+ "ctr",
+ "digest",
+ "hex",
+ "hmac",
+ "pbkdf2 0.11.0",
+ "rand",
+ "scrypt",
+ "serde",
+ "serde_json",
+ "sha2",
+ "sha3",
+ "thiserror",
+ "uuid 0.8.2",
+]
+
+[[package]]
+name = "ethnum"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
+
+[[package]]
+name = "eventsource-client"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9146112ee3ce031aa5aebe3e049e10b1d353b9c7630cc6be488c2c62cc5d9c42"
+dependencies = [
+ "futures",
+ "hyper",
+ "hyper-rustls 0.22.1",
+ "hyper-timeout",
+ "log",
+ "pin-project",
+ "tokio",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+
+[[package]]
+name = "ff"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
+dependencies = [
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e"
+
+[[package]]
+name = "fixed-hash"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[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 = "fuel-abi-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8118789261e77d67569859a06a886d53dbf7bd00ea23a18a2dfae26a1f5be25"
+dependencies = [
+ "itertools 0.10.5",
+ "lazy_static",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "serde",
+ "serde_json",
+ "syn 2.0.60",
+ "thiserror",
+]
+
+[[package]]
+name = "fuel-asm"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ea884860261efdc7300b63db7972cb0e08e8f5379495ad7cdd2bdb7c0cc4623"
+dependencies = [
+ "bitflags 2.5.0",
+ "fuel-types",
+ "serde",
+ "strum",
+]
+
+[[package]]
+name = "fuel-core"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5212499e280344967732bf7dc31227f02460e8c2947e7965d5703ad33d80c9ea"
+dependencies = [
+ "anyhow",
+ "async-graphql",
+ "async-trait",
+ "axum",
+ "clap",
+ "derive_more",
+ "enum-iterator",
+ "fuel-core-chain-config",
+ "fuel-core-consensus-module",
+ "fuel-core-database",
+ "fuel-core-executor",
+ "fuel-core-importer",
+ "fuel-core-metrics",
+ "fuel-core-poa",
+ "fuel-core-producer",
+ "fuel-core-services",
+ "fuel-core-storage",
+ "fuel-core-txpool",
+ "fuel-core-types",
+ "futures",
+ "hex",
+ "hyper",
+ "itertools 0.10.5",
+ "postcard",
+ "rand",
+ "serde",
+ "serde_json",
+ "strum",
+ "strum_macros",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tower-http",
+ "tracing",
+ "uuid 1.8.0",
+]
+
+[[package]]
+name = "fuel-core-chain-config"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ab93dc93c87c0c380e94a6a8d1b65e791151d2f6a567c4970fc8cf76faaa05"
+dependencies = [
+ "anyhow",
+ "bech32",
+ "fuel-core-storage",
+ "fuel-core-types",
+ "hex",
+ "itertools 0.10.5",
+ "postcard",
+ "serde",
+ "serde_json",
+ "serde_with 1.14.0",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-client"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a10b6a6e2dcc651f52961ef3c1bf44ab28434fdb437789bf17f4389d19041f4"
+dependencies = [
+ "anyhow",
+ "cynic",
+ "derive_more",
+ "eventsource-client",
+ "fuel-core-types",
+ "futures",
+ "hex",
+ "hyper-rustls 0.24.2",
+ "itertools 0.10.5",
+ "reqwest",
+ "schemafy_lib",
+ "serde",
+ "serde_json",
+ "tai64",
+ "thiserror",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-consensus-module"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61adeb3c3afc83a38616850dd84a32ffd5047bcc60d32b1d2848922c936ab5ec"
+dependencies = [
+ "anyhow",
+ "fuel-core-chain-config",
+ "fuel-core-poa",
+ "fuel-core-types",
+ "tokio",
+]
+
+[[package]]
+name = "fuel-core-database"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8aa43ee5fbd0a5710c8cde4e405cff4d729e5c48b4ba15714c07dbe45c7ef31"
+dependencies = [
+ "anyhow",
+ "derive_more",
+ "fuel-core-storage",
+ "fuel-core-types",
+]
+
+[[package]]
+name = "fuel-core-executor"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7ffd6011ac5628aed96de774bb30d8918d4d0cde5d82dd3619874655a3b130c"
+dependencies = [
+ "anyhow",
+ "fuel-core-chain-config",
+ "fuel-core-storage",
+ "fuel-core-types",
+ "hex",
+ "parking_lot",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-importer"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f9bc11dd9a5a112dffc84554af45aeff35021eb90fbbaa3da8280e659c0e32d"
+dependencies = [
+ "anyhow",
+ "derive_more",
+ "fuel-core-metrics",
+ "fuel-core-storage",
+ "fuel-core-types",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-metrics"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a08d775335cdfee3717faa083714c75da294508a1244fe7b229748c9926d91c3"
+dependencies = [
+ "axum",
+ "once_cell",
+ "pin-project-lite",
+ "prometheus-client 0.18.1",
+ "prometheus-client 0.20.0",
+ "regex",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-poa"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e99ef1eaab07450c327abb4809246f851cc0dc5d52650662a239bc66c26ded41"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "fuel-core-chain-config",
+ "fuel-core-services",
+ "fuel-core-storage",
+ "fuel-core-types",
+ "tokio",
+ "tokio-stream",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-producer"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c38073316314824e1865c269c6663f678003d48ae0a135397087fc1ebdd303e"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "derive_more",
+ "fuel-core-storage",
+ "fuel-core-types",
+ "tokio",
+ "tokio-rayon",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-services"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a42f05bc2ab4a91afd2db6556521002119e29de49744bd29c8f43b5f561d89"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "fuel-core-metrics",
+ "futures",
+ "parking_lot",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-storage"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95d4d9ede89f97c779433e389739410a946e8b94f379a0048a49670bef73e602"
+dependencies = [
+ "anyhow",
+ "derive_more",
+ "fuel-core-types",
+ "fuel-vm",
+ "primitive-types",
+]
+
+[[package]]
+name = "fuel-core-txpool"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d353895abdbb02f1e2b4b3ccbeadf3258d352d9150787fb7ca04933ea05834f"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "fuel-core-chain-config",
+ "fuel-core-metrics",
+ "fuel-core-services",
+ "fuel-core-storage",
+ "fuel-core-types",
+ "futures",
+ "parking_lot",
+ "tokio",
+ "tokio-rayon",
+ "tokio-stream",
+ "tracing",
+]
+
+[[package]]
+name = "fuel-core-types"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b75785b3a26f7c2c05e73e5cef2f59912751c4821b40225e089199c7fb8712d7"
+dependencies = [
+ "anyhow",
+ "bs58",
+ "derive_more",
+ "fuel-vm",
+ "secrecy",
+ "serde",
+ "tai64",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "fuel-crypto"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e0efe99de550a5b5c12a6a4d2eadd26bc5571cfba82d0133baa2805d485ad8c"
+dependencies = [
+ "coins-bip32",
+ "coins-bip39",
+ "ecdsa",
+ "ed25519-dalek",
+ "fuel-types",
+ "k256",
+ "lazy_static",
+ "p256",
+ "rand",
+ "secp256k1",
+ "serde",
+ "sha2",
+ "zeroize",
+]
+
+[[package]]
+name = "fuel-derive"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff58cf4d01a4fb9440c63a8764154dfd3b07c74e4b3639cce8eea77d67e63a7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "synstructure",
+]
+
+[[package]]
+name = "fuel-merkle"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89143dd80b29dda305fbb033bc7f868834445ef6b361bf920f0077938fb6c0bc"
+dependencies = [
+ "derive_more",
+ "digest",
+ "fuel-storage",
+ "hashbrown 0.13.2",
+ "hex",
+ "serde",
+ "sha2",
+]
+
+[[package]]
+name = "fuel-storage"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "901aee4b46684e483d2c04d40e5ac1b8ccda737ac5a363507b44b9eb23b0fdaa"
+
+[[package]]
+name = "fuel-tx"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb1f65e363e5e9a5412cea204f2d2357043327a0c3da5482c3b38b9da045f20e"
+dependencies = [
+ "bitflags 2.5.0",
+ "derivative",
+ "derive_more",
+ "fuel-asm",
+ "fuel-crypto",
+ "fuel-merkle",
+ "fuel-types",
+ "hashbrown 0.14.3",
+ "itertools 0.10.5",
+ "rand",
+ "serde",
+ "serde_json",
+ "strum",
+ "strum_macros",
+]
+
+[[package]]
+name = "fuel-types"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "148b59be5c54bafff692310663cbce3f097a2a7ff5533224dcfdf387578a72b0"
+dependencies = [
+ "fuel-derive",
+ "hex",
+ "rand",
+ "serde",
+]
+
+[[package]]
+name = "fuel-vm"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aed5ba0cde904f16cd748dc9b33e62f4b3dc5fd0a72ec867c973e687cd7347ba"
+dependencies = [
+ "async-trait",
+ "backtrace",
+ "bitflags 2.5.0",
+ "derivative",
+ "derive_more",
+ "ethnum",
+ "fuel-asm",
+ "fuel-crypto",
+ "fuel-merkle",
+ "fuel-storage",
+ "fuel-tx",
+ "fuel-types",
+ "hashbrown 0.14.3",
+ "itertools 0.10.5",
+ "libm",
+ "paste",
+ "percent-encoding",
+ "primitive-types",
+ "serde",
+ "sha3",
+ "static_assertions",
+ "strum",
+ "tai64",
+]
+
+[[package]]
+name = "fuels"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38897acad45f2d7a89a00cb8937982176e622881cd200a3482673ddce26cb906"
+dependencies = [
+ "fuel-core",
+ "fuel-core-client",
+ "fuel-crypto",
+ "fuel-tx",
+ "fuels-accounts",
+ "fuels-core",
+ "fuels-macros",
+ "fuels-programs",
+ "fuels-test-helpers",
+]
+
+[[package]]
+name = "fuels-accounts"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05228cc135d39665a33eca3bfa440e595f4038e89f6401d265ace1ebf07ad897"
+dependencies = [
+ "async-trait",
+ "chrono",
+ "elliptic-curve",
+ "eth-keystore",
+ "fuel-core-client",
+ "fuel-crypto",
+ "fuel-tx",
+ "fuel-types",
+ "fuels-core",
+ "hex",
+ "rand",
+ "semver",
+ "tai64",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "zeroize",
+]
+
+[[package]]
+name = "fuels-code-gen"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84609f3a2ecf5d6ab843a2ac5eed7910276b1f965e9b53b4c08db65d05b2ea23"
+dependencies = [
+ "Inflector",
+ "fuel-abi-types",
+ "itertools 0.12.1",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "serde_json",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "fuels-core"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bc9e186d686c0baecc63db3faa7690f96fe0b45544a9fc10351226872109117"
+dependencies = [
+ "async-trait",
+ "bech32",
+ "chrono",
+ "fuel-abi-types",
+ "fuel-asm",
+ "fuel-core-chain-config",
+ "fuel-core-client",
+ "fuel-crypto",
+ "fuel-tx",
+ "fuel-types",
+ "fuel-vm",
+ "fuels-macros",
+ "hex",
+ "itertools 0.12.1",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+ "uint",
+]
+
+[[package]]
+name = "fuels-macros"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "022f92bd0309e4929b87f9ca6f6731843c3056da7a1b8028ce6cced96e239b47"
+dependencies = [
+ "fuels-code-gen",
+ "itertools 0.12.1",
+ "proc-macro2",
+ "quote",
+ "rand",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "fuels-programs"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f941848fcf536be82abc2ab1bb73e3acfa9830ae93c1f911f853e149e58bce2"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "fuel-abi-types",
+ "fuel-asm",
+ "fuel-tx",
+ "fuel-types",
+ "fuels-accounts",
+ "fuels-core",
+ "itertools 0.12.1",
+ "rand",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "fuels-test-helpers"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63d03455eb05d3fd9a102ecba9cc769cd84f8d4f64b00b29e74cab961b3a4997"
+dependencies = [
+ "fuel-core",
+ "fuel-core-chain-config",
+ "fuel-core-client",
+ "fuel-core-poa",
+ "fuel-core-services",
+ "fuel-crypto",
+ "fuel-tx",
+ "fuel-types",
+ "fuels-accounts",
+ "fuels-core",
+ "futures",
+ "hex",
+ "portpicker",
+ "rand",
+ "serde",
+ "serde_json",
+ "serde_with 3.7.0",
+ "tempfile",
+ "tokio",
+ "which",
+]
+
+[[package]]
+name = "funty"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+ "zeroize",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "graphql-parser"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474"
+dependencies = [
+ "combine",
+ "thiserror",
+]
+
+[[package]]
+name = "group"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+dependencies = [
+ "ff",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap 2.2.6",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hash32"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "serde",
+]
+
+[[package]]
+name = "heapless"
+version = "0.7.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
+dependencies = [
+ "atomic-polyfill",
+ "hash32",
+ "rustc_version",
+ "serde",
+ "spin 0.9.8",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-range-header"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
+dependencies = [
+ "ct-logs",
+ "futures-util",
+ "hyper",
+ "log",
+ "rustls 0.19.1",
+ "rustls-native-certs 0.5.0",
+ "tokio",
+ "tokio-rustls 0.22.0",
+ "webpki",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
+dependencies = [
+ "futures-util",
+ "http",
+ "hyper",
+ "log",
+ "rustls 0.21.11",
+ "rustls-native-certs 0.6.3",
+ "tokio",
+ "tokio-rustls 0.24.1",
+ "webpki-roots",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
+dependencies = [
+ "bytes",
+ "hyper",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "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 = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[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.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.3",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "k256"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "once_cell",
+ "sha2",
+ "signature",
+]
+
+[[package]]
+name = "keccak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "matchit"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "multer"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
+dependencies = [
+ "bytes",
+ "encoding_rs",
+ "futures-util",
+ "http",
+ "httparse",
+ "log",
+ "memchr",
+ "mime",
+ "spin 0.9.8",
+ "version_check",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[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.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "openssl"
+version = "0.10.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+dependencies = [
+ "bitflags 2.5.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "p256"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+dependencies = [
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+
+[[package]]
+name = "pbkdf2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest",
+ "hmac",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pest"
+version = "2.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95"
+dependencies = [
+ "memchr",
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "platforms"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7"
+
+[[package]]
+name = "portpicker"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "postcard"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
+dependencies = [
+ "cobs",
+ "embedded-io",
+ "heapless",
+ "serde",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "primeorder"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+dependencies = [
+ "elliptic-curve",
+]
+
+[[package]]
+name = "primitive-types"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2"
+dependencies = [
+ "fixed-hash",
+ "uint",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prometheus-client"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c"
+dependencies = [
+ "dtoa",
+ "itoa",
+ "parking_lot",
+ "prometheus-client-derive-text-encode",
+]
+
+[[package]]
+name = "prometheus-client"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e227aeb6c2cfec819e999c4773b35f8c7fa37298a203ff46420095458eee567e"
+dependencies = [
+ "dtoa",
+ "itoa",
+ "parking_lot",
+ "prometheus-client-derive-encode",
+]
+
+[[package]]
+name = "prometheus-client-derive-encode"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "prometheus-client-derive-text-encode"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
+[[package]]
+name = "publicsuffix"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
+dependencies = [
+ "idna 0.3.0",
+ "psl-types",
+]
+
+[[package]]
+name = "pyth_sdk"
+version = "0.1.0"
+dependencies = [
+ "base64 0.22.0",
+ "dotenv",
+ "fuels",
+ "hex",
+ "rand",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radium"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "reqwest"
+version = "0.11.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
+dependencies = [
+ "base64 0.21.7",
+ "bytes",
+ "cookie",
+ "cookie_store",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-rustls 0.24.2",
+ "hyper-tls",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls 0.21.11",
+ "rustls-pemfile",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "system-configuration",
+ "tokio",
+ "tokio-native-tls",
+ "tokio-rustls 0.24.1",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots",
+ "winreg",
+]
+
+[[package]]
+name = "rfc6979"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+dependencies = [
+ "hmac",
+ "subtle",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin 0.5.2",
+ "untrusted 0.7.1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin 0.9.8",
+ "untrusted 0.9.0",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "ripemd"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
+dependencies = [
+ "base64 0.13.1",
+ "log",
+ "ring 0.16.20",
+ "sct 0.6.1",
+ "webpki",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
+dependencies = [
+ "log",
+ "ring 0.17.8",
+ "rustls-webpki",
+ "sct 0.7.1",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
+dependencies = [
+ "openssl-probe",
+ "rustls 0.19.1",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64 0.21.7",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring 0.17.8",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "salsa20"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "schemafy_core"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41781ae092f4fd52c9287efb74456aea0d3b90032d2ecad272bd14dbbcb0511b"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemafy_lib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e953db32579999ca98c451d80801b6f6a7ecba6127196c5387ec0774c528befa"
+dependencies = [
+ "Inflector",
+ "proc-macro2",
+ "quote",
+ "schemafy_core",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "scrypt"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d"
+dependencies = [
+ "hmac",
+ "pbkdf2 0.11.0",
+ "salsa20",
+ "sha2",
+]
+
+[[package]]
+name = "sct"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
+dependencies = [
+ "ring 0.16.20",
+ "untrusted 0.7.1",
+]
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring 0.17.8",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "sec1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+dependencies = [
+ "base16ct",
+ "der",
+ "generic-array",
+ "pkcs8",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "secp256k1"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894"
+dependencies = [
+ "rand",
+ "secp256k1-sys",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "secrecy"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e"
+dependencies = [
+ "zeroize",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.198"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.198"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.116"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[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 = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
+dependencies = [
+ "serde",
+ "serde_with_macros",
+]
+
+[[package]]
+name = "serde_with"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
+dependencies = [
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
+dependencies = [
+ "darling 0.13.4",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha3"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest",
+ "rand_core",
+]
+
+[[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.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tai64"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed7401421025f4132e6c1f7af5e7f8287383969f36e6628016cd509b8d3da9dc"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+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.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-io-timeout"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rayon"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cf33a76e0b1dd03b778f83244137bd59887abf25c0e87bc3e7071105f457693"
+dependencies = [
+ "rayon",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
+dependencies = [
+ "rustls 0.19.1",
+ "tokio",
+ "webpki",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls 0.21.11",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap 2.2.6",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
+dependencies = [
+ "bitflags 1.3.2",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-range-header",
+ "pin-project-lite",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tracing-futures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
+dependencies = [
+ "futures",
+ "futures-task",
+ "pin-project",
+ "tracing",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+
+[[package]]
+name = "uint"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52"
+dependencies = [
+ "byteorder",
+ "crunchy",
+ "hex",
+ "static_assertions",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unreachable"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
+dependencies = [
+ "void",
+]
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna 0.5.0",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+dependencies = [
+ "getrandom",
+ "serde",
+]
+
+[[package]]
+name = "uuid"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[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.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
+dependencies = [
+ "ring 0.16.20",
+ "untrusted 0.7.1",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.25.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+
+[[package]]
+name = "which"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[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.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wyz"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+dependencies = [
+ "tap",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]

+ 27 - 0
target_chains/fuel/contracts/Cargo.toml

@@ -0,0 +1,27 @@
+[package]
+name = "pyth_sdk"
+description = "A cargo-generate template for Rust + Sway integration testing."
+version = "0.1.0"
+edition = "2021"
+authors = ["Fuel Labs <contact@fuel.sh>"]
+license = "Apache-2.0"
+
+[dependencies]
+rand = "0.8.5"
+base64 = "0.22"
+fuels = { version = "0.55", features = ["fuel-core-lib"] }
+tokio = { version = "1.12", features = ["rt", "macros"] }
+hex = "0.4.3"
+reqwest = "0.11.27"
+serde_json = "1.0.114"
+serde = "1.0.197"
+dotenv = "0.15.0"
+
+[[bin]]
+name = "deploy_pyth"
+path = "scripts/deploy_pyth.rs"
+
+[[test]]
+harness = true
+name = "integration_tests"
+path = "tests/harness.rs"

+ 44 - 0
target_chains/fuel/contracts/Forc.lock

@@ -0,0 +1,44 @@
+[[package]]
+name = "core"
+source = "path+from-root-C3992B43B72ADB8C"
+
+[[package]]
+name = "ownership"
+source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.18.0#8d196e9379463d4596ac582a20a84ed52ff58c69"
+dependencies = [
+    "src_5",
+    "std",
+]
+
+[[package]]
+name = "pyth-contract"
+source = "member"
+dependencies = [
+    "ownership",
+    "pyth_interface",
+    "src5",
+    "std",
+]
+
+[[package]]
+name = "pyth_interface"
+source = "path+from-root-555D3D27A908977B"
+dependencies = [
+    "src5",
+    "std",
+]
+
+[[package]]
+name = "src5"
+source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd"
+dependencies = ["std"]
+
+[[package]]
+name = "src_5"
+source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.2.2#6989cf8224b0d8aabea62f3d3c648fc754948705"
+dependencies = ["std"]
+
+[[package]]
+name = "std"
+source = "git+https://github.com/fuellabs/sway?tag=v0.49.1#2ac7030570f22510b0ac2a7b5ddf7baa20bdc0e1"
+dependencies = ["core"]

+ 2 - 0
target_chains/fuel/contracts/Forc.toml

@@ -0,0 +1,2 @@
+[workspace]
+members = ["./pyth-contract"]

+ 201 - 0
target_chains/fuel/contracts/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 44 - 0
target_chains/fuel/contracts/README.md

@@ -0,0 +1,44 @@
+# Pyth-integration
+
+> **_NOTE:_** The project is a WIP.
+
+An implementation of a [Pyth Network](https://pyth.network/) oracle contract in Sway. Utilising minimal, internal [Wormhole](https://docs.wormhole.com/wormhole/) functionality and state.
+
+## Interfaces
+
+The project provides four interfaces for interaction with the oracle contract:
+
+- [PythCore](./pyth-interface/src/interface.sw#L20) - provides the core functionality to required to utilise the oracle; getting fees, updating prices and fetching prices.
+- [PythInit](./pyth-interface/src/interface.sw#L250) - provides the functionality to setup the oracle's state.
+- [PythInfo](./pyth-interface/src/interface.sw#L255) - provides additional information about the oracle's state.
+- [WormholeGuardians](./pyth-interface/src/interface.sw#L283) - provides functionality to maintain and query the wormhole-state-elements used by the oracle.
+
+## Running the project
+
+### Project
+
+Run the following commands from the root of the repository.
+
+#### Program compilation
+
+```bash
+forc build
+```
+
+#### Running the tests
+
+Before running the tests the programs must be compiled with the command above.
+
+```bash
+cargo test
+```
+
+#### Before deploying
+
+Before deploying the oracle contract; the `deployer` must be set to the address of the deploying wallet in the storage block, so that the deployer can setup the contract with the `constructor()` method.
+
+Parameters for the `constructor()` method can be seen in the [tests of the method](./pyth-contract/tests/functions/pyth_init/constuctor.rs#L28), which at the time of writing uses the real up-to-date values as per Pyth's documentation and EVM integrations. Care should be taken to ensure that the most up-to-date values are used for the `constructor()` method's parameters.
+
+#### Fuel Beta-5 network deployment:
+
+The Pyth oracle contract has been deployed to Beta-5 at the `ContractId`: 0xe69daeb9fcf4c536c0fe402403b4b9e9822cc8b1f296e5d754be12cc384554c5.

+ 6 - 0
target_chains/fuel/contracts/fuel-toolchain.toml

@@ -0,0 +1,6 @@
+[toolchain]
+channel = "nightly-2024-01-24"
+
+[components]
+forc = "0.49.1"
+fuel-core = "0.22.0"

+ 10 - 0
target_chains/fuel/contracts/pyth-contract/Forc.toml

@@ -0,0 +1,10 @@
+[project]
+authors = ["Fuel Labs <contact@fuel.sh>"]
+entry = "main.sw"
+license = "Apache-2.0"
+name = "pyth-contract"
+
+[dependencies]
+ownership = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.18.0" }
+pyth_interface = { path = "../pyth-interface" }
+src5 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" }

+ 8 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures.sw

@@ -0,0 +1,8 @@
+library;
+
+pub mod data_source;
+pub mod wormhole_light;
+pub mod price;
+pub mod accumulator_update;
+pub mod batch_attestation_update;
+pub mod update_type;

+ 133 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures/accumulator_update.sw

@@ -0,0 +1,133 @@
+library;
+
+use ::errors::PythError;
+use ::data_structures::{data_source::*, price::*, wormhole_light::{StorageGuardianSet, WormholeVM}};
+use pyth_interface::data_structures::{data_source::DataSource, price::{PriceFeed, PriceFeedId}};
+use std::{bytes::Bytes, hash::Hash};
+
+pub struct AccumulatorUpdate {
+    data: Bytes,
+}
+const MINIMUM_ALLOWED_MINOR_VERSION = 0;
+const MAJOR_VERSION = 1;
+impl AccumulatorUpdate {
+    pub fn new(data: Bytes) -> Self {
+        Self { data }
+    }
+    pub fn total_updates(self, ref mut offset: u64) -> u64 {
+        let proof_size = u16::from_be_bytes([self.data.get(offset).unwrap(), self.data.get(offset + 1).unwrap()]).as_u64();
+        offset += proof_size + 2;
+        self.data.get(offset).unwrap().as_u64()
+    }
+    pub fn verify(self) -> u64 {
+        // skip magic as already checked when this is called
+        let major_version = self.data.get(4);
+        require(
+            major_version
+                .is_some() && major_version
+                .unwrap() == MAJOR_VERSION,
+            PythError::InvalidMajorVersion,
+        );
+        let minor_version = self.data.get(5);
+        require(
+            minor_version
+                .is_some() && minor_version
+                .unwrap() >= MINIMUM_ALLOWED_MINOR_VERSION,
+            PythError::InvalidMinorVersion,
+        );
+        let trailing_header_size = self.data.get(6);
+        require(trailing_header_size.is_some(), PythError::InvalidHeaderSize);
+        // skip trailing headers and update type
+        let offset = 8 + trailing_header_size.unwrap().as_u64();
+        require(self.data.len >= offset, PythError::InvalidUpdateDataLength);
+        offset
+    }
+}
+impl AccumulatorUpdate {
+    #[storage(read)]
+    pub fn verify_and_parse(
+        self,
+        current_guardian_set_index: u32,
+        wormhole_guardian_sets: StorageKey<StorageMap<u32, StorageGuardianSet>>,
+        is_valid_data_source: StorageKey<StorageMap<DataSource, bool>>,
+) -> (u64, Bytes, u64, Bytes) {
+        let encoded_offset = self.verify();
+        let (_, slice) = self.data.split_at(encoded_offset);
+        let (encoded_slice, _) = slice.split_at(self.data.len - encoded_offset);
+        let mut offset = 0;
+        let wormhole_proof_size = u16::from_be_bytes([encoded_slice.get(offset).unwrap(), encoded_slice.get(offset + 1).unwrap()]).as_u64();
+        offset += 2;
+        let (_, slice) = encoded_slice.split_at(offset);
+        let (encoded_vm, _) = slice.split_at(wormhole_proof_size);
+        let vm = WormholeVM::parse_and_verify_pyth_vm(
+            current_guardian_set_index,
+            encoded_vm,
+            wormhole_guardian_sets,
+            is_valid_data_source,
+        );
+        offset += wormhole_proof_size;
+        let encoded_payload = vm.payload;
+        /*
+        Payload offset:
+        skip magic (4 bytes) as already checked when this is called
+        skip update_type as (1 byte) it can only be WormholeMerkle
+        skip slot (8 bytes) as unused
+        skip ring_size (4 bytes) as unused
+        */
+        let mut payload_offset = 17;
+        let (_, slice) = encoded_payload.split_at(payload_offset);
+        let (digest, _) = slice.split_at(20);
+        payload_offset += 20;
+        require(
+            payload_offset <= encoded_payload
+                .len,
+            PythError::InvalidPayloadLength,
+        );
+        let number_of_updates = encoded_slice.get(offset);
+        require(
+            number_of_updates
+                .is_some(),
+            PythError::NumberOfUpdatesIrretrievable,
+        );
+        offset += 1;
+        (offset, digest, number_of_updates.unwrap().as_u64(), encoded_slice)
+    }
+}
+impl AccumulatorUpdate {
+    #[storage(read, write)]
+    pub fn update_price_feeds(
+        self,
+        current_guardian_set_index: u32,
+        wormhole_guardian_sets: StorageKey<StorageMap<u32, StorageGuardianSet>>,
+        latest_price_feed: StorageKey<StorageMap<PriceFeedId, PriceFeed>>,
+        is_valid_data_source: StorageKey<StorageMap<DataSource, bool>>,
+) -> (u64, Vec<PriceFeedId>) {
+        let (mut offset, digest, number_of_updates, encoded_data) = self.verify_and_parse(
+            current_guardian_set_index,
+            wormhole_guardian_sets,
+            is_valid_data_source,
+        );
+
+        let mut updated_ids = Vec::new();
+        let mut i = 0;
+        while i < number_of_updates {
+            let (new_offset, price_feed) = PriceFeed::extract_from_merkle_proof(digest, encoded_data, offset);
+            offset = new_offset;
+            let latest_publish_time = match latest_price_feed.get(price_feed.id).try_read() {
+                Some(price_feed) => price_feed.price.publish_time,
+                None => 0,
+            };
+            if price_feed.price.publish_time > latest_publish_time {
+                latest_price_feed.insert(price_feed.id, price_feed);
+                updated_ids.push(price_feed.id);
+            }
+            i += 1;
+        }
+        require(
+            offset == encoded_data
+                .len,
+            PythError::InvalidUpdateDataLength,
+        );
+        (number_of_updates, updated_ids)
+    }
+}

+ 95 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures/batch_attestation_update.sw

@@ -0,0 +1,95 @@
+library;
+
+use ::errors::PythError;
+use ::data_structures::{data_source::*, price::*, wormhole_light::{StorageGuardianSet, WormholeVM}};
+use pyth_interface::data_structures::{data_source::DataSource, price::{PriceFeed, PriceFeedId}};
+use std::{bytes::Bytes, hash::Hash};
+
+const BATCH_MAGIC: u32 = 0x50325748;
+
+pub struct BatchAttestationUpdate {
+    data: Bytes,
+}
+impl BatchAttestationUpdate {
+    pub fn new(data: Bytes) -> Self {
+        Self { data }
+    }
+    #[storage(read, write)]
+    pub fn update_price_feeds(
+        self,
+        current_guardian_set_index: u32,
+        wormhole_guardian_sets: StorageKey<StorageMap<u32, StorageGuardianSet>>,
+        latest_price_feed: StorageKey<StorageMap<PriceFeedId, PriceFeed>>,
+        is_valid_data_source: StorageKey<StorageMap<DataSource, bool>>,
+) -> Vec<PriceFeedId> {
+        let vm = WormholeVM::parse_and_verify_pyth_vm(
+            current_guardian_set_index,
+            self.data,
+            wormhole_guardian_sets,
+            is_valid_data_source,
+        );
+        let (mut attestation_index, number_of_attestations, attestation_size) = parse_and_verify_batch_attestation_header(vm.payload);
+        let mut updated_ids = Vec::new();
+        let mut i: u16 = 0;
+        while i < number_of_attestations {
+            let price_feed = PriceFeed::parse_attestation(attestation_size, vm.payload, attestation_index);
+            // Respect specified attestation size for forward-compatibility
+            attestation_index += attestation_size.as_u64();
+            let latest_publish_time = match latest_price_feed.get(price_feed.id).try_read() {
+                Some(price_feed) => price_feed.price.publish_time,
+                None => 0,
+            };
+            if price_feed.price.publish_time > latest_publish_time {
+                latest_price_feed.insert(price_feed.id, price_feed);
+                updated_ids.push(price_feed.id);
+            }
+            i += 1;
+        }
+        updated_ids
+    }
+}
+pub fn parse_and_verify_batch_attestation_header(encoded_payload: Bytes) -> (u64, u16, u16) {
+    let mut index = 0;
+    //Check header
+    let magic = u32::from_be_bytes([
+        encoded_payload.get(index).unwrap(),
+        encoded_payload.get(index + 1).unwrap(),
+        encoded_payload.get(index + 2).unwrap(),
+        encoded_payload.get(index + 3).unwrap(),
+    ]);
+    require(magic == BATCH_MAGIC, PythError::InvalidMagic);
+    index += 4;
+    let major_version = u16::from_be_bytes([encoded_payload.get(index).unwrap(), encoded_payload.get(index + 1).unwrap()]);
+    require(major_version == 3, PythError::InvalidMajorVersion);
+    // addtionally skip minor_version(2 bytes) as unused
+    index += 4;
+    let header_size = u16::from_be_bytes([encoded_payload.get(index).unwrap(), encoded_payload.get(index + 1).unwrap()]);
+    index += 2;
+    // From solidity impl:
+    // NOTE(2022-04-19): Currently, only payloadId comes after
+    // hdrSize. Future extra header fields must be read using a
+    // separate offset to respect hdrSize, i.e.:
+    // uint hdrIndex = 0;
+    // bpa.header.payloadId = UnsafeBytesLib.toUint8(encoded, index + hdrIndex);
+    // hdrIndex += 1;
+    // bpa.header.someNewField = UnsafeBytesLib.toUint32(encoded, index + hdrIndex);
+    // hdrIndex += 4;
+    // Skip remaining unknown header bytes
+    // index += bpa.header.hdrSize;
+    let payload_id = encoded_payload.get(index).unwrap();
+    // Payload ID of 2 required for batch header
+    require(payload_id == 2, PythError::InvalidPayloadId);
+    // Skip remaining unknown header bytes
+    index += header_size.as_u64();
+    let number_of_attestations = u16::from_be_bytes([encoded_payload.get(index).unwrap(), encoded_payload.get(index + 1).unwrap()]);
+    index += 2;
+    let attestation_size = u16::from_be_bytes([encoded_payload.get(index).unwrap(), encoded_payload.get(index + 1).unwrap()]);
+    index += 2;
+    require(
+        encoded_payload
+            .len == index + (attestation_size * number_of_attestations)
+            .as_u64(),
+        PythError::InvalidPayloadLength,
+    );
+    return (index, number_of_attestations, attestation_size);
+}

+ 31 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw

@@ -0,0 +1,31 @@
+library;
+
+use pyth_interface::data_structures::data_source::DataSource;
+use std::hash::{Hash, Hasher};
+
+impl Hash for DataSource {
+    fn hash(self, ref mut state: Hasher) {
+        self.chain_id.hash(state);
+        self.emitter_address.hash(state);
+    }
+}
+
+impl DataSource {
+    pub fn new(chain_id: u16, emitter_address: b256) -> Self {
+        Self {
+            chain_id,
+            emitter_address,
+        }
+    }
+
+    #[storage(read)]
+    pub fn is_valid(
+        self,
+        is_valid_data_source: StorageKey<StorageMap<DataSource, bool>>,
+) -> bool {
+        match is_valid_data_source.get(self).try_read() {
+            Some(bool) => bool,
+            None => false,
+        }
+    }
+}

+ 311 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures/price.sw

@@ -0,0 +1,311 @@
+library;
+
+use pyth_interface::data_structures::price::{Price, PriceFeed, PriceFeedId};
+use std::bytes::Bytes;
+use ::errors::PythError;
+use ::utils::absolute_of_exponent;
+use ::pyth_merkle_proof::validate_proof;
+use ::data_structures::wormhole_light::WormholeVM;
+
+const TAI64_DIFFERENCE = 4611686018427387904;
+
+impl Price {
+    pub fn new(
+        confidence: u64,
+        exponent: u32,
+        price: u64,
+        publish_time: u64,
+    ) -> Self {
+        Self {
+            confidence,
+            exponent,
+            price,
+            publish_time,
+        }
+    }
+}
+
+impl PriceFeedId {
+    pub fn is_target(self, target_price_feed_ids: Vec<PriceFeedId>) -> bool {
+        let mut i = 0;
+        while i < target_price_feed_ids.len {
+            if target_price_feed_ids.get(i).unwrap() == self {
+                return true;
+            }
+            i += 1;
+        }
+        false
+    }
+    pub fn is_contained_within(self, output_price_feeds: Vec<PriceFeed>) -> bool {
+        let mut i = 0;
+        while i < output_price_feeds.len {
+            if output_price_feeds.get(i).unwrap().id == self {
+                return true;
+            }
+            i += 1;
+        }
+        false
+    }
+}
+
+impl PriceFeed {
+    pub fn new(ema_price: Price, id: PriceFeedId, price: Price) -> Self {
+        Self {
+            ema_price,
+            id,
+            price,
+        }
+    }
+}
+
+impl PriceFeed {
+    pub fn parse_message(encoded_price_feed: Bytes) -> Self {
+        let mut offset = 1u64;
+        let (_, slice) = encoded_price_feed.split_at(offset);
+        let (price_feed_id, _) = slice.split_at(32);
+        let price_feed_id: PriceFeedId = price_feed_id.into();
+        offset += 32;
+        let price = u64::from_be_bytes([
+            encoded_price_feed.get(offset).unwrap(),
+            encoded_price_feed.get(offset + 1).unwrap(),
+            encoded_price_feed.get(offset + 2).unwrap(),
+            encoded_price_feed.get(offset + 3).unwrap(),
+            encoded_price_feed.get(offset + 4).unwrap(),
+            encoded_price_feed.get(offset + 5).unwrap(),
+            encoded_price_feed.get(offset + 6).unwrap(),
+            encoded_price_feed.get(offset + 7).unwrap(),
+        ]);
+        offset += 8;
+        let confidence = u64::from_be_bytes([
+            encoded_price_feed.get(offset).unwrap(),
+            encoded_price_feed.get(offset + 1).unwrap(),
+            encoded_price_feed.get(offset + 2).unwrap(),
+            encoded_price_feed.get(offset + 3).unwrap(),
+            encoded_price_feed.get(offset + 4).unwrap(),
+            encoded_price_feed.get(offset + 5).unwrap(),
+            encoded_price_feed.get(offset + 6).unwrap(),
+            encoded_price_feed.get(offset + 7).unwrap(),
+        ]);
+        offset += 8;
+        // exponent is an i32, expected to be in the range -255 to 0
+        let exponent = u32::from_be_bytes([
+            encoded_price_feed.get(offset).unwrap(),
+            encoded_price_feed.get(offset + 1).unwrap(),
+            encoded_price_feed.get(offset + 2).unwrap(),
+            encoded_price_feed.get(offset + 3).unwrap(),
+        ]);
+        let exponent = absolute_of_exponent(exponent);
+        require(exponent < 256u32, PythError::InvalidExponent);
+        offset += 4;
+        let mut publish_time = u64::from_be_bytes([
+            encoded_price_feed.get(offset).unwrap(),
+            encoded_price_feed.get(offset + 1).unwrap(),
+            encoded_price_feed.get(offset + 2).unwrap(),
+            encoded_price_feed.get(offset + 3).unwrap(),
+            encoded_price_feed.get(offset + 4).unwrap(),
+            encoded_price_feed.get(offset + 5).unwrap(),
+            encoded_price_feed.get(offset + 6).unwrap(),
+            encoded_price_feed.get(offset + 7).unwrap(),
+        ]);
+        // skip unused previous_publish_times (8 bytes)
+        offset += 16;
+        let ema_price = u64::from_be_bytes([
+            encoded_price_feed.get(offset).unwrap(),
+            encoded_price_feed.get(offset + 1).unwrap(),
+            encoded_price_feed.get(offset + 2).unwrap(),
+            encoded_price_feed.get(offset + 3).unwrap(),
+            encoded_price_feed.get(offset + 4).unwrap(),
+            encoded_price_feed.get(offset + 5).unwrap(),
+            encoded_price_feed.get(offset + 6).unwrap(),
+            encoded_price_feed.get(offset + 7).unwrap(),
+        ]);
+        offset += 8;
+        let ema_confidence = u64::from_be_bytes([
+            encoded_price_feed.get(offset).unwrap(),
+            encoded_price_feed.get(offset + 1).unwrap(),
+            encoded_price_feed.get(offset + 2).unwrap(),
+            encoded_price_feed.get(offset + 3).unwrap(),
+            encoded_price_feed.get(offset + 4).unwrap(),
+            encoded_price_feed.get(offset + 5).unwrap(),
+            encoded_price_feed.get(offset + 6).unwrap(),
+            encoded_price_feed.get(offset + 7).unwrap(),
+        ]);
+        offset += 8;
+        require(
+            offset <= encoded_price_feed
+                .len,
+            PythError::InvalidPriceFeedDataLength,
+        );
+        //convert publish_time from UNIX to TAI64
+        publish_time += TAI64_DIFFERENCE;
+
+        PriceFeed::new(
+            Price::new(ema_confidence, exponent, ema_price, publish_time),
+            price_feed_id,
+            Price::new(confidence, exponent, price, publish_time),
+        )
+    }
+    pub fn parse_attestation(attestation_size: u16, encoded_payload: Bytes, index: u64) -> Self {
+        // Skip product id (32 bytes) as unused
+        let mut attestation_index = index + 32;
+        let (_, slice) = encoded_payload.split_at(attestation_index);
+        let (price_feed_id, _) = slice.split_at(32);
+        let price_feed_id: PriceFeedId = price_feed_id.into();
+        attestation_index += 32;
+        let mut price = u64::from_be_bytes([
+            encoded_payload.get(attestation_index).unwrap(),
+            encoded_payload.get(attestation_index + 1).unwrap(),
+            encoded_payload.get(attestation_index + 2).unwrap(),
+            encoded_payload.get(attestation_index + 3).unwrap(),
+            encoded_payload.get(attestation_index + 4).unwrap(),
+            encoded_payload.get(attestation_index + 5).unwrap(),
+            encoded_payload.get(attestation_index + 6).unwrap(),
+            encoded_payload.get(attestation_index + 7).unwrap(),
+        ]);
+        attestation_index += 8;
+        let mut confidence = u64::from_be_bytes([
+            encoded_payload.get(attestation_index).unwrap(),
+            encoded_payload.get(attestation_index + 1).unwrap(),
+            encoded_payload.get(attestation_index + 2).unwrap(),
+            encoded_payload.get(attestation_index + 3).unwrap(),
+            encoded_payload.get(attestation_index + 4).unwrap(),
+            encoded_payload.get(attestation_index + 5).unwrap(),
+            encoded_payload.get(attestation_index + 6).unwrap(),
+            encoded_payload.get(attestation_index + 7).unwrap(),
+        ]);
+        attestation_index += 8;
+        // exponent is an i32, expected to be in the range -255 to 0
+        let exponent = u32::from_be_bytes([
+            encoded_payload.get(attestation_index).unwrap(),
+            encoded_payload.get(attestation_index + 1).unwrap(),
+            encoded_payload.get(attestation_index + 2).unwrap(),
+            encoded_payload.get(attestation_index + 3).unwrap(),
+        ]);
+        let exponent = absolute_of_exponent(exponent);
+        require(exponent < 256u32, PythError::InvalidExponent);
+        attestation_index += 4;
+        let ema_price = u64::from_be_bytes([
+            encoded_payload.get(attestation_index).unwrap(),
+            encoded_payload.get(attestation_index + 1).unwrap(),
+            encoded_payload.get(attestation_index + 2).unwrap(),
+            encoded_payload.get(attestation_index + 3).unwrap(),
+            encoded_payload.get(attestation_index + 4).unwrap(),
+            encoded_payload.get(attestation_index + 5).unwrap(),
+            encoded_payload.get(attestation_index + 6).unwrap(),
+            encoded_payload.get(attestation_index + 7).unwrap(),
+        ]);
+        attestation_index += 8;
+        let ema_confidence = u64::from_be_bytes([
+            encoded_payload.get(attestation_index).unwrap(),
+            encoded_payload.get(attestation_index + 1).unwrap(),
+            encoded_payload.get(attestation_index + 2).unwrap(),
+            encoded_payload.get(attestation_index + 3).unwrap(),
+            encoded_payload.get(attestation_index + 4).unwrap(),
+            encoded_payload.get(attestation_index + 5).unwrap(),
+            encoded_payload.get(attestation_index + 6).unwrap(),
+            encoded_payload.get(attestation_index + 7).unwrap(),
+        ]);
+        attestation_index += 8;
+        // Status is an enum (encoded as u8) with the following values:
+        // 0 = UNKNOWN: The price feed is not currently updating for an unknown reason.
+        // 1 = TRADING: The price feed is updating as expected.
+        // 2 = HALTED: The price feed is not currently updating because trading in the product has been halted.
+        // 3 = AUCTION: The price feed is not currently updating because an auction is setting the price.
+        let status = encoded_payload.get(attestation_index).unwrap();
+        // Additionally skip number_of publishers (8 bytes) and attestation_time (8 bytes); as unused
+        attestation_index += 17;
+        let mut publish_time = u64::from_be_bytes([
+            encoded_payload.get(attestation_index).unwrap(),
+            encoded_payload.get(attestation_index + 1).unwrap(),
+            encoded_payload.get(attestation_index + 2).unwrap(),
+            encoded_payload.get(attestation_index + 3).unwrap(),
+            encoded_payload.get(attestation_index + 4).unwrap(),
+            encoded_payload.get(attestation_index + 5).unwrap(),
+            encoded_payload.get(attestation_index + 6).unwrap(),
+            encoded_payload.get(attestation_index + 7).unwrap(),
+        ]);
+        attestation_index += 8;
+        if status == 1u8 {
+            attestation_index += 24;
+        } else {
+            // If status is not trading then the latest available price is
+            // the previous price that is parsed here.
+
+            // previous publish time
+            publish_time = u64::from_be_bytes([
+                encoded_payload.get(attestation_index).unwrap(),
+                encoded_payload.get(attestation_index + 1).unwrap(),
+                encoded_payload.get(attestation_index + 2).unwrap(),
+                encoded_payload.get(attestation_index + 3).unwrap(),
+                encoded_payload.get(attestation_index + 4).unwrap(),
+                encoded_payload.get(attestation_index + 5).unwrap(),
+                encoded_payload.get(attestation_index + 6).unwrap(),
+                encoded_payload.get(attestation_index + 7).unwrap(),
+            ]);
+            attestation_index += 8;
+            // previous price
+            price = u64::from_be_bytes([
+                encoded_payload.get(attestation_index).unwrap(),
+                encoded_payload.get(attestation_index + 1).unwrap(),
+                encoded_payload.get(attestation_index + 2).unwrap(),
+                encoded_payload.get(attestation_index + 3).unwrap(),
+                encoded_payload.get(attestation_index + 4).unwrap(),
+                encoded_payload.get(attestation_index + 5).unwrap(),
+                encoded_payload.get(attestation_index + 6).unwrap(),
+                encoded_payload.get(attestation_index + 7).unwrap(),
+            ]);
+            attestation_index += 8;
+            // previous confidence
+            confidence = u64::from_be_bytes([
+                encoded_payload.get(attestation_index).unwrap(),
+                encoded_payload.get(attestation_index + 1).unwrap(),
+                encoded_payload.get(attestation_index + 2).unwrap(),
+                encoded_payload.get(attestation_index + 3).unwrap(),
+                encoded_payload.get(attestation_index + 4).unwrap(),
+                encoded_payload.get(attestation_index + 5).unwrap(),
+                encoded_payload.get(attestation_index + 6).unwrap(),
+                encoded_payload.get(attestation_index + 7).unwrap(),
+            ]);
+            attestation_index += 8;
+        }
+        require(
+            (attestation_index - index) <= attestation_size
+                .as_u64(),
+            PythError::InvalidAttestationSize,
+        );
+        //convert publish_time from UNIX to TAI64
+        publish_time += TAI64_DIFFERENCE;
+
+        PriceFeed::new(
+            Price::new(ema_confidence, exponent, ema_price, publish_time),
+            price_feed_id,
+            Price::new(confidence, exponent, price, publish_time),
+        )
+    }
+}
+
+impl PriceFeed {
+    pub fn extract_from_merkle_proof(digest: Bytes, encoded_proof: Bytes, offset: u64) -> (u64, self) {
+        // In order to avoid `ref mut` param related MemoryWriteOverlap error
+        let mut current_offset = offset;
+
+        let message_size = u16::from_be_bytes([
+            encoded_proof.get(current_offset).unwrap(),
+            encoded_proof.get(current_offset + 1).unwrap(),
+        ]).as_u64();
+        current_offset += 2;
+        let (_, slice) = encoded_proof.split_at(current_offset);
+        let (encoded_message, _) = slice.split_at(message_size);
+        current_offset += message_size;
+        let end_offset = validate_proof(encoded_proof, encoded_message, current_offset, digest);
+        // Message type of 0 is a Price Feed
+        require(
+            encoded_message
+                .get(0)
+                .unwrap() == 0,
+            PythError::IncorrectMessageType,
+        );
+        let price_feed = PriceFeed::parse_message(encoded_message);
+        (end_offset, price_feed)
+    }
+}

+ 37 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures/update_type.sw

@@ -0,0 +1,37 @@
+library;
+
+use std::{array_conversions::u32::*, bytes::Bytes};
+use ::data_structures::{
+    accumulator_update::AccumulatorUpdate,
+    batch_attestation_update::BatchAttestationUpdate,
+};
+
+const ACCUMULATOR_MAGIC: u32 = 0x504e4155;
+
+pub enum UpdateType {
+    Accumulator: AccumulatorUpdate,
+    BatchAttestation: BatchAttestationUpdate,
+}
+
+impl UpdateType {
+    pub fn determine_type(data: Bytes) -> Self {
+        let (magic, _) = data.split_at(4); //TODO: Convert to u32 for comparison with const ACCUMULATOR_MAGIC. Use raw_ptr.read::<u32>()? Remove accumulator_magic_bytes()
+        if data.len > 4 && magic == accumulator_magic_bytes() {
+            UpdateType::Accumulator(AccumulatorUpdate::new(data))
+        } else {
+            UpdateType::BatchAttestation((BatchAttestationUpdate::new(data)))
+        }
+    }
+}
+
+pub fn accumulator_magic_bytes() -> Bytes {
+    let accumulator_magic_array = ACCUMULATOR_MAGIC.to_be_bytes();
+
+    let mut accumulator_magic_bytes = Bytes::with_capacity(4);
+    accumulator_magic_bytes.push(accumulator_magic_array[0]);
+    accumulator_magic_bytes.push(accumulator_magic_array[1]);
+    accumulator_magic_bytes.push(accumulator_magic_array[2]);
+    accumulator_magic_bytes.push(accumulator_magic_array[3]);
+
+    accumulator_magic_bytes
+}

+ 590 - 0
target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw

@@ -0,0 +1,590 @@
+library;
+
+use ::data_structures::data_source::*;
+use ::errors::WormholeError;
+use pyth_interface::data_structures::{
+    data_source::DataSource,
+    wormhole_light::{
+        GuardianSet,
+        WormholeProvider,
+    },
+};
+use std::{
+    array_conversions::{
+        b256::*,
+        u16::*,
+        u32::*,
+    },
+    b512::B512,
+    block::timestamp,
+    bytes::Bytes,
+    constants::ZERO_B256,
+    hash::{
+        Hash,
+        keccak256,
+        sha256,
+    },
+    storage::storage_vec::*,
+    vm::evm::ecr::ec_recover_evm_address,
+};
+
+pub const UPGRADE_MODULE: b256 = 0x00000000000000000000000000000000000000000000000000000000436f7265;
+
+impl GuardianSet {
+    #[storage(read)]
+    pub fn from_stored(stored: StorageGuardianSet) -> Self {
+        Self {
+            expiration_time: stored.expiration_time,
+            keys: stored.keys.load_vec(),
+        }
+    }
+}
+
+pub struct StorageGuardianSet {
+    expiration_time: u64,
+    keys: StorageKey<StorageVec<b256>>,
+}
+
+impl StorageGuardianSet {
+    pub fn new(expiration_time: u64, keys: StorageKey<StorageVec<b256>>) -> Self {
+        StorageGuardianSet {
+            expiration_time,
+            keys,
+        }
+    }
+}
+
+pub struct GuardianSetUpgrade {
+    action: u8,
+    chain: u16,
+    module: b256,
+    new_guardian_set: StorageGuardianSet,
+    new_guardian_set_index: u32,
+}
+
+impl GuardianSetUpgrade {
+    pub fn new(
+        action: u8,
+        chain: u16,
+        module: b256,
+        new_guardian_set: StorageGuardianSet,
+        new_guardian_set_index: u32,
+    ) -> Self {
+        GuardianSetUpgrade {
+            action,
+            chain,
+            module,
+            new_guardian_set,
+            new_guardian_set_index,
+        }
+    }
+}
+
+impl GuardianSetUpgrade {
+    #[storage(read, write)]
+    pub fn parse_encoded_upgrade(current_guardian_set_index: u32, encoded_upgrade: Bytes) -> Self {
+        let mut index = 0;
+        let (_, slice) = encoded_upgrade.split_at(index);
+        let (module, _) = slice.split_at(32);
+        let module: b256 = module.into();
+        require(module == UPGRADE_MODULE, WormholeError::InvalidModule);
+        index += 32;
+        let action = encoded_upgrade.get(index).unwrap();
+        require(action == 2, WormholeError::InvalidGovernanceAction);
+        index += 1;
+        let chain = u16::from_be_bytes([encoded_upgrade.get(index).unwrap(), encoded_upgrade.get(index + 1).unwrap()]);
+        index += 2;
+        let new_guardian_set_index = u32::from_be_bytes([
+            encoded_upgrade.get(index).unwrap(),
+            encoded_upgrade.get(index + 1).unwrap(),
+            encoded_upgrade.get(index + 2).unwrap(),
+            encoded_upgrade.get(index + 3).unwrap(),
+        ]);
+        require(
+            new_guardian_set_index > current_guardian_set_index,
+            WormholeError::NewGuardianSetIndexIsInvalid,
+        );
+        index += 4;
+        let guardian_length = encoded_upgrade.get(index).unwrap();
+        index += 1;
+        let mut new_guardian_set = StorageGuardianSet::new(
+            0,
+            StorageKey {
+                slot: sha256(("guardian_set_keys", new_guardian_set_index)),
+                offset: 0,
+                field_id: ZERO_B256,
+            },
+        );
+        let mut i: u8 = 0;
+        while i < guardian_length {
+            let (_, slice) = encoded_upgrade.split_at(index);
+            let (key, _) = slice.split_at(20);
+            let key: b256 = key.into();
+            new_guardian_set.keys.push(key.rsh(96));
+            index += 20;
+            i += 1;
+        }
+        require(
+            new_guardian_set
+                .keys
+                .len() > 0,
+            WormholeError::NewGuardianSetIsEmpty,
+        );
+        require(
+            encoded_upgrade
+                .len == index,
+            WormholeError::InvalidGuardianSetUpgradeLength,
+        );
+        GuardianSetUpgrade::new(
+            action,
+            chain,
+            module,
+            new_guardian_set,
+            new_guardian_set_index,
+        )
+    }
+}
+
+impl WormholeProvider {
+    pub fn new(governance_chain_id: u16, governance_contract: b256) -> Self {
+        WormholeProvider {
+            governance_chain_id,
+            governance_contract,
+        }
+    }
+}
+
+pub struct GuardianSignature {
+    guardian_index: u8,
+    r: b256,
+    s: b256,
+    v: u8,
+}
+
+impl GuardianSignature {
+    pub fn new(guardian_index: u8, r: b256, s: b256, v: u8) -> Self {
+        GuardianSignature {
+            guardian_index,
+            r,
+            s,
+            v,
+        }
+    }
+    // eip-2098: Compact Signature Representation
+    pub fn compact(self) -> B512 {
+        let y_parity = b256::from_be_bytes([
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            0u8,
+            self.v - 27u8,
+        ]);
+        let shifted_y_parity = y_parity.lsh(255);
+        let y_parity_and_s = b256::binary_or(shifted_y_parity, self.s);
+        B512::from((self.r, y_parity_and_s))
+    }
+}
+
+impl GuardianSignature {
+    pub fn verify(
+        self,
+        guardian_set_key: b256,
+        hash: b256,
+        index: u64,
+        last_index: u64,
+) {
+        // Ensure that provided signature indices are ascending only
+        if index > 0 {
+            require(
+                self.guardian_index
+                    .as_u64() > last_index,
+                WormholeError::SignatureIndicesNotAscending,
+            );
+        }
+        let recovered_signer = ec_recover_evm_address(self.compact(), hash);
+        require(
+            recovered_signer
+                .is_ok() && recovered_signer
+                .unwrap()
+                .value == guardian_set_key,
+            WormholeError::SignatureInvalid,
+        );
+    }
+}
+
+pub struct WormholeVM {
+    version: u8,
+    guardian_set_index: u32,
+    governance_action_hash: b256,
+    // signatures: Vec<GuardianSignature>, // Shown here to represent data layout of VM, but not needed
+    timestamp: u32,
+    nonce: u32,
+    emitter_chain_id: u16,
+    emitter_address: b256,
+    sequence: u64,
+    consistency_level: u8,
+    payload: Bytes,
+}
+
+impl WormholeVM {
+    pub fn default() -> Self {
+        WormholeVM {
+            version: 0u8,
+            guardian_set_index: 0u32,
+            governance_action_hash: ZERO_B256,
+            timestamp: 0u32,
+            nonce: 0u32,
+            emitter_chain_id: 0u16,
+            emitter_address: ZERO_B256,
+            sequence: 0u64,
+            consistency_level: 0u8,
+            payload: Bytes::new(),
+        }
+    }
+    pub fn new(
+        version: u8,
+        guardian_set_index: u32,
+        governance_action_hash: b256,
+        timestamp_: u32,
+        nonce: u32,
+        emitter_chain_id: u16,
+        emitter_address: b256,
+        sequence: u64,
+        consistency_level: u8,
+        payload: Bytes,
+    ) -> Self {
+        WormholeVM {
+            version,
+            guardian_set_index,
+            governance_action_hash,
+            timestamp: timestamp_,
+            nonce,
+            emitter_chain_id,
+            emitter_address,
+            sequence,
+            consistency_level,
+            payload,
+        }
+    }
+}
+
+impl WormholeVM {
+    #[storage(read)]
+    pub fn parse_and_verify_wormhole_vm(
+        current_guardian_set_index: u32,
+        encoded_vm: Bytes,
+        wormhole_guardian_sets: StorageKey<StorageMap<u32, StorageGuardianSet>>,
+    ) -> Self {
+        let mut index = 0;
+        let version = encoded_vm.get(index);
+        require(
+            version
+                .is_some() && version
+                .unwrap() == 1,
+            WormholeError::VMVersionIncompatible,
+        );
+        index += 1;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(4); //replace with slice()
+        let guardian_set_index = u32::from_be_bytes([
+            //replace with func
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+        ]);
+        index += 4;
+        let guardian_set = wormhole_guardian_sets.get(guardian_set_index).try_read();
+        require(guardian_set.is_some(), WormholeError::GuardianSetNotFound);
+        let guardian_set = guardian_set.unwrap();
+        require(
+            guardian_set
+                .keys
+                .len() > 0,
+            WormholeError::InvalidGuardianSetKeysLength,
+        );
+        require(
+            guardian_set_index == current_guardian_set_index && (guardian_set
+                    .expiration_time == 0 || guardian_set
+                    .expiration_time > timestamp()),
+            WormholeError::InvalidGuardianSet,
+        );
+        let signers_length = encoded_vm.get(index);
+        require(
+            signers_length
+                .is_some(),
+            WormholeError::SignersLengthIrretrievable,
+        );
+        let signers_length = signers_length.unwrap().as_u64();
+        index += 1;
+        // 66 is the length of each guardian signature
+        // 1 (guardianIndex) + 32 (r) + 32 (s) + 1 (v)
+        let hash_index = index + (signers_length * 66);
+        require(
+            hash_index < encoded_vm
+                .len,
+            WormholeError::InvalidSignatureLength,
+        );
+        let (_, slice) = encoded_vm.split_at(hash_index);
+        let hash = keccak256(keccak256(slice));
+        let mut last_index = 0;
+        let mut i = 0;
+        while i < signers_length {
+            let guardian_index = encoded_vm.get(index);
+            require(
+                guardian_index
+                    .is_some(),
+                WormholeError::GuardianIndexIrretrievable,
+            );
+            let guardian_index = guardian_index.unwrap();
+            index += 1;
+            let (_, slice) = encoded_vm.split_at(index);
+            let (slice, remainder) = slice.split_at(32);
+            let r: b256 = slice.into();
+            index += 32;
+            let (slice, remainder) = remainder.split_at(32);
+            let s: b256 = slice.into();
+            index += 32;
+            let v = remainder.get(0);
+            require(v.is_some(), WormholeError::SignatureVIrretrievable);
+            let v = v.unwrap() + 27;
+            index += 1;
+            let guardian_set_key = guardian_set.keys.get(guardian_index.as_u64());
+            require(
+                guardian_set_key
+                    .is_some(),
+                WormholeError::GuardianSetKeyIrretrievable,
+            );
+            GuardianSignature::new(guardian_index, r, s, v)
+                .verify(guardian_set_key.unwrap().read(), hash, i, last_index);
+            last_index = guardian_index.as_u64();
+            i += 1;
+        }
+        /*
+        We're using a fixed point number transformation with 1 decimal to deal with rounding.
+        This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM.
+        If guardian set key length is 0 and signatures length is 0, this could compromise the integrity of both VM and signature verification.
+        */
+        require(
+            ((((guardian_set
+                                .keys
+                                .len() * 10) / 3) * 2) / 10 + 1) <= signers_length,
+            WormholeError::NoQuorum,
+        );
+        //ignore VM.signatures
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(4);
+        let _timestamp = u32::from_be_bytes([
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+        ]);
+        index += 4;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(4);
+        let nonce = u32::from_be_bytes([
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+        ]);
+        index += 4;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(2);
+        let emitter_chain_id = u16::from_be_bytes([slice.get(0).unwrap(), slice.get(1).unwrap()]);
+        index += 2;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(32);
+        let emitter_address: b256 = slice.into();
+        index += 32;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(8);
+        let sequence = u64::from_be_bytes([
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+            slice.get(4).unwrap(),
+            slice.get(5).unwrap(),
+            slice.get(6).unwrap(),
+            slice.get(7).unwrap(),
+        ]);
+        index += 8;
+        let consistency_level = encoded_vm.get(index);
+        require(
+            consistency_level
+                .is_some(),
+            WormholeError::ConsistencyLevelIrretrievable,
+        );
+        index += 1;
+        require(index <= encoded_vm.len, WormholeError::InvalidPayloadLength);
+        let (_, payload) = encoded_vm.split_at(index);
+        WormholeVM::new(
+            version
+                .unwrap(),
+            guardian_set_index,
+            hash,
+            _timestamp,
+            nonce,
+            emitter_chain_id,
+            emitter_address,
+            sequence,
+            consistency_level
+                .unwrap(),
+            payload,
+        )
+    }
+    pub fn parse_initial_wormhole_vm(encoded_vm: Bytes) -> Self {
+        let mut index = 0;
+        let version = encoded_vm.get(index);
+        require(
+            version
+                .is_some() && version
+                .unwrap() == 1,
+            WormholeError::VMVersionIncompatible,
+        );
+        index += 1;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(4); //replace with slice()
+        let guardian_set_index = u32::from_be_bytes([
+            //replace with func
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+        ]);
+        index += 4;
+        let signers_length = encoded_vm.get(index);
+        require(
+            signers_length
+                .is_some(),
+            WormholeError::SignersLengthIrretrievable,
+        );
+        let signers_length = signers_length.unwrap().as_u64();
+        index += 1;
+        // 66 is the length of each guardian signature
+        // 1 (guardianIndex) + 32 (r) + 32 (s) + 1 (v)
+        let hash_index = index + (signers_length * 66);
+        require(
+            hash_index < encoded_vm
+                .len,
+            WormholeError::InvalidSignatureLength,
+        );
+        let (_, slice) = encoded_vm.split_at(hash_index);
+        let hash = keccak256(keccak256(slice));
+        // account for signatures
+        index += 66 * signers_length;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(4);
+        let timestamp_ = u32::from_be_bytes([
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+        ]);
+        index += 4;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(4);
+        let nonce = u32::from_be_bytes([
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+        ]);
+        index += 4;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(2);
+        let emitter_chain_id = u16::from_be_bytes([slice.get(0).unwrap(), slice.get(1).unwrap()]);
+        index += 2;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(32);
+        let emitter_address: b256 = slice.into();
+        index += 32;
+        let (_, slice) = encoded_vm.split_at(index);
+        let (slice, _) = slice.split_at(8);
+        let sequence = u64::from_be_bytes([
+            slice.get(0).unwrap(),
+            slice.get(1).unwrap(),
+            slice.get(2).unwrap(),
+            slice.get(3).unwrap(),
+            slice.get(4).unwrap(),
+            slice.get(5).unwrap(),
+            slice.get(6).unwrap(),
+            slice.get(7).unwrap(),
+        ]);
+        index += 8;
+        let consistency_level = encoded_vm.get(index);
+        require(
+            consistency_level
+                .is_some(),
+            WormholeError::ConsistencyLevelIrretrievable,
+        );
+        index += 1;
+        require(index <= encoded_vm.len, WormholeError::InvalidPayloadLength);
+        let (_, payload) = encoded_vm.split_at(index);
+        WormholeVM::new(
+            version
+                .unwrap(),
+            guardian_set_index,
+            hash,
+            timestamp_,
+            nonce,
+            emitter_chain_id,
+            emitter_address,
+            sequence,
+            consistency_level
+                .unwrap(),
+            payload,
+        )
+    }
+}
+
+impl WormholeVM {
+    #[storage(read)]
+    pub fn parse_and_verify_pyth_vm(
+        current_guardian_set_index: u32,
+        encoded_vm: Bytes,
+        wormhole_guardian_sets: StorageKey<StorageMap<u32, StorageGuardianSet>>,
+        is_valid_data_source: StorageKey<StorageMap<DataSource, bool>>,
+    ) -> Self {
+        let vm = WormholeVM::parse_and_verify_wormhole_vm(
+            current_guardian_set_index,
+            encoded_vm,
+            wormhole_guardian_sets,
+        );
+        require(
+            DataSource::new(vm.emitter_chain_id, vm.emitter_address)
+                .is_valid(is_valid_data_source),
+            WormholeError::InvalidUpdateDataSource,
+        );
+        vm
+    }
+}

+ 63 - 0
target_chains/fuel/contracts/pyth-contract/src/errors.sw

@@ -0,0 +1,63 @@
+library;
+
+pub enum PythError {
+    FeesCanOnlyBePaidInTheBaseAsset: (),
+    GuardianSetNotFound: (),
+    IncorrectMessageType: (),
+    InsufficientFee: (),
+    InvalidArgument: (),
+    InvalidAttestationSize: (),
+    InvalidDataSourcesLength: (),
+    InvalidExponent: (),
+    InvalidHeaderSize: (),
+    InvalidMagic: (),
+    InvalidMajorVersion: (),
+    InvalidMinorVersion: (),
+    InvalidPayloadId: (),
+    InvalidPayloadLength: (),
+    InvalidPriceFeedDataLength: (),
+    InvalidProof: (),
+    InvalidUpdateData: (),
+    InvalidUpdateDataLength: (),
+    InvalidUpdateDataSource: (),
+    InvalidUpgradeModule: (),
+    LengthOfPriceFeedIdsAndPublishTimesMustMatch: (),
+    NewGuardianSetIsEmpty: (),
+    NumberOfUpdatesIrretrievable: (),
+    /// Emitted when a Price's `publish_time` is stale.
+    OutdatedPrice: (),
+    /// Emitted when a PriceFeed could not be retrieved.
+    PriceFeedNotFound: (),
+    PriceFeedNotFoundWithinRange: (),
+    WormholeGovernanceActionNotFound: (),
+}
+
+pub enum WormholeError {
+    ConsistencyLevelIrretrievable: (),
+    GovernanceActionAlreadyConsumed: (),
+    GuardianIndexIrretrievable: (),
+    GuardianSetHasExpired: (),
+    GuardianSetKeyIrretrievable: (),
+    GuardianSetNotFound: (),
+    InvalidGovernanceAction: (),
+    InvalidGovernanceChain: (),
+    InvalidGovernanceContract: (),
+    InvalidGuardianSet: (),
+    InvalidGuardianSetKeysLength: (),
+    InvalidGuardianSetUpgrade: (),
+    InvalidGuardianSetUpgradeLength: (),
+    InvalidModule: (),
+    InvalidPayloadLength: (),
+    InvalidSignatureLength: (),
+    InvalidUpdateDataSource: (),
+    NewGuardianSetIsEmpty: (),
+    NewGuardianSetIndexIsInvalid: (),
+    NoQuorum: (),
+    NotSignedByCurrentGuardianSet: (),
+    SignatureInvalid: (),
+    SignatureIndicesNotAscending: (),
+    SignatureVIrretrievable: (),
+    SignersLengthIrretrievable: (),
+    VMSignatureInvalid: (),
+    VMVersionIncompatible: (),
+}

+ 21 - 0
target_chains/fuel/contracts/pyth-contract/src/events.sw

@@ -0,0 +1,21 @@
+library;
+
+use pyth_interface::data_structures::{
+    data_source::DataSource,
+    price::PriceFeedId,
+    wormhole_light::WormholeProvider,
+};
+
+pub struct ConstructedEvent {
+    guardian_set_index: u32,
+}
+
+pub struct NewGuardianSetEvent {
+    governance_action_hash: b256,
+    // new_guardian_set: GuardianSet, // TODO: Uncomment when SDK supports logs with nested Vecs https://github.com/FuelLabs/fuels-rs/issues/1046
+    new_guardian_set_index: u32,
+}
+
+pub struct UpdatedPriceFeedsEvent {
+    updated_price_feeds: Vec<PriceFeedId>,
+}

+ 617 - 0
target_chains/fuel/contracts/pyth-contract/src/main.sw

@@ -0,0 +1,617 @@
+contract;
+
+mod errors;
+mod utils;
+mod pyth_merkle_proof;
+mod data_structures;
+mod events;
+
+use std::{
+    block::timestamp,
+    bytes::Bytes,
+    call_frames::msg_asset_id,
+    constants::{
+        BASE_ASSET_ID,
+        ZERO_B256,
+    },
+    context::msg_amount,
+    hash::Hash,
+    storage::{
+        storage_map::StorageMap,
+        storage_vec::*,
+    },
+    u256::U256,
+};
+
+use ::errors::{PythError, WormholeError};
+use ::utils::{difference, total_fee};
+use ::data_structures::{
+    batch_attestation_update::parse_and_verify_batch_attestation_header,
+    data_source::*,
+    price::*,
+    update_type::UpdateType,
+    wormhole_light::*,
+};
+use ::events::{ConstructedEvent, NewGuardianSetEvent, UpdatedPriceFeedsEvent};
+
+use pyth_interface::{
+    data_structures::{
+        data_source::DataSource,
+        price::{
+            Price,
+            PriceFeed,
+            PriceFeedId,
+        },
+        wormhole_light::{
+            GuardianSet,
+            WormholeProvider,
+        },
+    },
+    PythCore,
+    PythInfo,
+    PythInit,
+    WormholeGuardians,
+};
+
+use ownership::*;
+use src5::{SRC5, State};
+
+configurable {
+    DEPLOYER: Identity = Identity::Address(Address::from(ZERO_B256)),
+}
+
+storage {
+    //   |                |
+    // --+-- PYTH STATE --+--
+    //   |                |
+    // (chainId, emitterAddress) => isValid; takes advantage of
+    // constant-time mapping lookup for VM verification
+    is_valid_data_source: StorageMap<DataSource, bool> = StorageMap {},
+    // Mapping of cached price information
+    // priceId => PriceInfo
+    latest_price_feed: StorageMap<PriceFeedId, PriceFeed> = StorageMap {},
+    single_update_fee: u64 = 0,
+    // For tracking all active emitter/chain ID pairs
+    valid_data_sources: StorageVec<DataSource> = StorageVec {},
+    /// Maximum acceptable time period before price is considered to be stale.
+    /// This includes attestation delay, block time, and potential clock drift
+    /// between the source/target chains.
+    valid_time_period_seconds: u64 = 0,
+    //   |                    |
+    // --+-- WORMHOLE STATE --+--
+    //   |                    |
+    // Mapping of consumed governance actions
+    wormhole_consumed_governance_actions: StorageMap<b256, bool> = StorageMap {},
+    // Mapping of guardian_set_index => guardian set
+    wormhole_guardian_sets: StorageMap<u32, StorageGuardianSet> = StorageMap {},
+    // Current active guardian set index
+    wormhole_guardian_set_index: u32 = 0,
+    // Using Ethereum's Wormhole governance
+    wormhole_provider: WormholeProvider = WormholeProvider {
+        governance_chain_id: 0u16,
+        governance_contract: ZERO_B256,
+    },
+}
+
+impl SRC5 for Contract {
+    #[storage(read)]
+    fn owner() -> State {
+        _owner()
+    }
+}
+
+impl PythCore for Contract {
+    #[storage(read)]
+    fn ema_price(price_feed_id: PriceFeedId) -> Price {
+        ema_price_no_older_than(valid_time_period(), price_feed_id)
+    }
+
+    #[storage(read)]
+    fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
+        ema_price_no_older_than(time_period, price_feed_id)
+    }
+
+    #[storage(read)]
+    fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price {
+        ema_price_unsafe(price_feed_id)
+    }
+
+    #[storage(read), payable]
+    fn parse_price_feed_updates(
+        max_publish_time: u64,
+        min_publish_time: u64,
+        target_price_feed_ids: Vec<PriceFeedId>,
+        update_data: Vec<Bytes>,
+    ) -> Vec<PriceFeed> {
+        require(
+            msg_asset_id() == BASE_ASSET_ID,
+            PythError::FeesCanOnlyBePaidInTheBaseAsset,
+        );
+
+        let required_fee = update_fee(update_data);
+        require(msg_amount() >= required_fee, PythError::InsufficientFee);
+
+        let mut output_price_feeds: Vec<PriceFeed> = Vec::with_capacity(target_price_feed_ids.len);
+        let mut i = 0;
+        while i < update_data.len {
+            let data = update_data.get(i).unwrap();
+
+            match UpdateType::determine_type(data) {
+                UpdateType::Accumulator(accumulator_update) => {
+                    let (mut offset, digest, number_of_updates, encoded) = accumulator_update.verify_and_parse(
+                        current_guardian_set_index(),
+                        storage
+                            .wormhole_guardian_sets,
+                        storage
+                            .is_valid_data_source,
+                    );
+                    let mut i_2 = 0;
+                    while i_2 < number_of_updates {
+                        let (new_offset, price_feed) = PriceFeed::extract_from_merkle_proof(digest, encoded, offset);
+
+                        offset = new_offset;
+
+                        if price_feed.id.is_target(target_price_feed_ids) == false {
+                            i_2 += 1;
+                            continue;
+                        }
+
+                        if price_feed.price.publish_time >= min_publish_time && price_feed.price.publish_time <= max_publish_time {
+                            // check if output_price_feeds already contains a PriceFeed with price_feed.id, if so continue as we only want 1
+                            // output PriceFeed per target ID
+                            if price_feed.id.is_contained_within(output_price_feeds) {
+                                i_2 += 1;
+                                continue;
+                            }
+
+                            output_price_feeds.push(price_feed)
+                        }
+
+                        i_2 += 1;
+                    }
+                    require(offset == encoded.len, PythError::InvalidUpdateDataLength);
+                },
+                UpdateType::BatchAttestation(batch_attestation_update) => {
+                    let vm = WormholeVM::parse_and_verify_pyth_vm(
+                        current_guardian_set_index(),
+                        batch_attestation_update
+                            .data,
+                        storage
+                            .wormhole_guardian_sets,
+                        storage
+                            .is_valid_data_source,
+                    );
+
+                    let (mut attestation_index, number_of_attestations, attestation_size) = parse_and_verify_batch_attestation_header(vm.payload);
+                    let attestation_size_u16 = attestation_size.as_u64();
+
+                    let mut i_2: u16 = 0;
+                    while i_2 < number_of_attestations {
+                        let (_, slice) = vm.payload.split_at(attestation_index + 32);
+                        let (price_feed_id, _) = slice.split_at(32);
+                        let price_feed_id: PriceFeedId = price_feed_id.into();
+
+                        if price_feed_id.is_target(target_price_feed_ids) == false {
+                            attestation_index += attestation_size_u16;
+                            i_2 += 1;
+                            continue;
+                        }
+
+                        let price_feed = PriceFeed::parse_attestation(attestation_size, vm.payload, attestation_index);
+
+                        if price_feed.price.publish_time >= min_publish_time && price_feed.price.publish_time <= max_publish_time {
+                            // check if output_price_feeds already contains a PriceFeed with price_feed.id, if so continue;
+                            // as we only want 1 output PriceFeed per target ID
+                            if price_feed.id.is_contained_within(output_price_feeds) {
+                                attestation_index += attestation_size_u16;
+                                i_2 += 1;
+                                continue;
+                            }
+
+                            output_price_feeds.push(price_feed)
+                        }
+
+                        attestation_index += attestation_size_u16;
+                        i_2 += 1;
+                    }
+                }
+            }
+
+            i += 1;
+        }
+
+        require(
+            target_price_feed_ids
+                .len == output_price_feeds
+                .len,
+            PythError::PriceFeedNotFoundWithinRange,
+        );
+
+        output_price_feeds
+    }
+
+    #[storage(read)]
+    fn price(price_feed_id: PriceFeedId) -> Price {
+        price_no_older_than(valid_time_period(), price_feed_id)
+    }
+
+    #[storage(read)]
+    fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
+        price_no_older_than(time_period, price_feed_id)
+    }
+
+    #[storage(read)]
+    fn price_unsafe(price_feed_id: PriceFeedId) -> Price {
+        price_unsafe(price_feed_id)
+    }
+
+    #[storage(read)]
+    fn update_fee(update_data: Vec<Bytes>) -> u64 {
+        update_fee(update_data)
+    }
+
+    #[storage(read, write), payable]
+    fn update_price_feeds(update_data: Vec<Bytes>) {
+        update_price_feeds(update_data)
+    }
+
+    #[storage(read, write), payable]
+    fn update_price_feeds_if_necessary(
+        price_feed_ids: Vec<PriceFeedId>,
+        publish_times: Vec<u64>,
+        update_data: Vec<Bytes>,
+    ) {
+        require(
+            price_feed_ids
+                .len == publish_times
+                .len,
+            PythError::LengthOfPriceFeedIdsAndPublishTimesMustMatch,
+        );
+
+        let mut i = 0;
+        while i < price_feed_ids.len {
+            if latest_publish_time(price_feed_ids.get(i).unwrap()) < publish_times.get(i).unwrap()
+            {
+                update_price_feeds(update_data);
+                return;
+            }
+
+            i += 1;
+        }
+    }
+
+    #[storage(read)]
+    fn valid_time_period() -> u64 {
+        valid_time_period()
+    }
+}
+
+/// PythCore Private Functions ///
+#[storage(read)]
+fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
+    let price = ema_price_unsafe(price_feed_id);
+
+    require(
+        difference(timestamp(), price.publish_time) <= time_period,
+        PythError::OutdatedPrice,
+    );
+
+    price
+}
+
+#[storage(read)]
+fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price {
+    let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
+    require(price_feed.is_some(), PythError::PriceFeedNotFound);
+
+    price_feed.unwrap().ema_price
+}
+
+#[storage(read)]
+fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
+    let price = price_unsafe(price_feed_id);
+    require(
+        difference(timestamp(), price.publish_time) <= time_period,
+        PythError::OutdatedPrice,
+    );
+
+    price
+}
+
+#[storage(read)]
+fn price_unsafe(price_feed_id: PriceFeedId) -> Price {
+    let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
+    require(price_feed.is_some(), PythError::PriceFeedNotFound);
+
+    price_feed.unwrap().price
+}
+
+#[storage(read)]
+fn update_fee(update_data: Vec<Bytes>) -> u64 {
+    let mut total_number_of_updates = 0;
+    let mut i = 0;
+    while i < update_data.len {
+        let data = update_data.get(i).unwrap();
+
+        match UpdateType::determine_type(data) {
+            UpdateType::Accumulator(accumulator_update) => {
+                let proof_size_offset = accumulator_update.verify();
+
+                total_number_of_updates += accumulator_update.total_updates(proof_size_offset);
+            },
+            UpdateType::BatchAttestation => {
+                total_number_of_updates += 1;
+            },
+        }
+
+        i += 1;
+    }
+
+    total_fee(total_number_of_updates, storage.single_update_fee)
+}
+
+#[storage(read, write), payable]
+fn update_price_feeds(update_data: Vec<Bytes>) {
+    require(
+        msg_asset_id() == BASE_ASSET_ID,
+        PythError::FeesCanOnlyBePaidInTheBaseAsset,
+    );
+
+    let mut total_number_of_updates = 0;
+
+    // let mut updated_price_feeds: Vec<PriceFeedId> = Vec::new(); // TODO: requires append for Vec
+    let mut i = 0;
+    while i < update_data.len {
+        let data = update_data.get(i).unwrap();
+
+        match UpdateType::determine_type(data) {
+            UpdateType::Accumulator(accumulator_update) => {
+                let (number_of_updates, _updated_ids) = accumulator_update.update_price_feeds(
+                    current_guardian_set_index(),
+                    storage
+                        .wormhole_guardian_sets,
+                    storage
+                        .latest_price_feed,
+                    storage
+                        .is_valid_data_source,
+                );
+                // updated_price_feeds.append(updated_ids); // TODO: requires append for Vec
+                total_number_of_updates += number_of_updates;
+            },
+            UpdateType::BatchAttestation(batch_attestation_update) => {
+                let _updated_ids = batch_attestation_update.update_price_feeds(
+                    current_guardian_set_index(),
+                    storage
+                        .wormhole_guardian_sets,
+                    storage
+                        .latest_price_feed,
+                    storage
+                        .is_valid_data_source,
+                );
+                // updated_price_feeds.append(updated_ids); // TODO: requires append for Vec
+                total_number_of_updates += 1;
+            },
+        }
+
+        i += 1;
+    }
+
+    let required_fee = total_fee(total_number_of_updates, storage.single_update_fee);
+    require(msg_amount() >= required_fee, PythError::InsufficientFee);
+
+    // log(UpdatedPriceFeedsEvent { // TODO: requires append for Vec
+    //     updated_price_feeds,
+    // })
+}
+
+#[storage(read)]
+fn valid_time_period() -> u64 {
+    storage.valid_time_period_seconds.read()
+}
+
+impl PythInit for Contract {
+    #[storage(read, write)]
+    fn constructor(
+        data_sources: Vec<DataSource>,
+        single_update_fee: u64,
+        valid_time_period_seconds: u64,
+        wormhole_guardian_set_upgrade: Bytes,
+    ) {
+        initialize_ownership(DEPLOYER);
+        only_owner();
+
+        require(data_sources.len > 0, PythError::InvalidDataSourcesLength);
+
+        let mut i = 0;
+        while i < data_sources.len {
+            let data_source = data_sources.get(i).unwrap();
+            storage.is_valid_data_source.insert(data_source, true);
+            storage.valid_data_sources.push(data_source);
+
+            i += 1;
+        }
+
+        storage
+            .valid_time_period_seconds
+            .write(valid_time_period_seconds);
+        storage.single_update_fee.write(single_update_fee);
+
+        let vm = WormholeVM::parse_initial_wormhole_vm(wormhole_guardian_set_upgrade);
+        let upgrade = GuardianSetUpgrade::parse_encoded_upgrade(0, vm.payload);
+
+        storage
+            .wormhole_consumed_governance_actions
+            .insert(vm.governance_action_hash, true);
+        storage
+            .wormhole_guardian_sets
+            .insert(upgrade.new_guardian_set_index, upgrade.new_guardian_set);
+        storage
+            .wormhole_guardian_set_index
+            .write(upgrade.new_guardian_set_index);
+        storage
+            .wormhole_provider
+            .write(WormholeProvider::new(vm.emitter_chain_id, vm.emitter_address));
+
+        renounce_ownership();
+
+        log(ConstructedEvent {
+            guardian_set_index: upgrade.new_guardian_set_index,
+        })
+    }
+}
+
+impl PythInfo for Contract {
+    #[storage(read)]
+    fn valid_data_sources() -> Vec<DataSource> {
+        storage.valid_data_sources.load_vec()
+    }
+
+    #[storage(read)]
+    fn latest_publish_time(price_feed_id: PriceFeedId) -> u64 {
+        latest_publish_time(price_feed_id)
+    }
+
+    #[storage(read)]
+    fn price_feed_exists(price_feed_id: PriceFeedId) -> bool {
+        match storage.latest_price_feed.get(price_feed_id).try_read() {
+            Some(_) => true,
+            None => false,
+        }
+    }
+
+    #[storage(read)]
+    fn price_feed_unsafe(price_feed_id: PriceFeedId) -> PriceFeed {
+        let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
+        require(price_feed.is_some(), PythError::PriceFeedNotFound);
+        price_feed.unwrap()
+    }
+
+    #[storage(read)]
+    fn single_update_fee() -> u64 {
+        storage.single_update_fee.read()
+    }
+
+    #[storage(read)]
+    fn valid_data_source(data_source: DataSource) -> bool {
+        data_source.is_valid(storage.is_valid_data_source)
+    }
+}
+
+/// PythInfo Private Functions ///
+#[storage(read)]
+fn latest_publish_time(price_feed_id: PriceFeedId) -> u64 {
+    match storage.latest_price_feed.get(price_feed_id).try_read() {
+        Some(price_feed) => price_feed.price.publish_time,
+        None => 0,
+    }
+}
+
+impl WormholeGuardians for Contract {
+    #[storage(read)]
+    fn current_guardian_set_index() -> u32 {
+        current_guardian_set_index()
+    }
+
+    #[storage(read)]
+    fn current_wormhole_provider() -> WormholeProvider {
+        current_wormhole_provider()
+    }
+
+    #[storage(read)]
+    fn guardian_set(index: u32) -> GuardianSet {
+        let stored_guardian_set = storage.wormhole_guardian_sets.get(index).try_read();
+        require(
+            stored_guardian_set
+                .is_some(),
+            PythError::GuardianSetNotFound,
+        );
+        GuardianSet::from_stored(stored_guardian_set.unwrap())
+    }
+
+    #[storage(read)]
+    fn governance_action_is_consumed(governance_action_hash: b256) -> bool {
+        governance_action_is_consumed(governance_action_hash)
+    }
+
+    #[storage(read, write)]
+    fn submit_new_guardian_set(encoded_vm: Bytes) {
+        submit_new_guardian_set(encoded_vm)
+    }
+}
+
+/// WormholeGuardians Private Functions ///
+#[storage(read)]
+fn current_guardian_set_index() -> u32 {
+    storage.wormhole_guardian_set_index.read()
+}
+
+#[storage(read)]
+fn current_wormhole_provider() -> WormholeProvider {
+    storage.wormhole_provider.read()
+}
+
+#[storage(read)]
+fn governance_action_is_consumed(governance_action_hash: b256) -> bool {
+    match storage.wormhole_consumed_governance_actions.get(governance_action_hash).try_read() {
+        Some(bool_) => bool_,
+        None => false,
+    }
+}
+
+#[storage(read, write)]
+fn submit_new_guardian_set(encoded_vm: Bytes) {
+    let vm = WormholeVM::parse_and_verify_wormhole_vm(
+        current_guardian_set_index(),
+        encoded_vm,
+        storage
+            .wormhole_guardian_sets,
+    );
+    require(
+        vm.guardian_set_index == current_guardian_set_index(),
+        WormholeError::NotSignedByCurrentGuardianSet,
+    );
+    let current_wormhole_provider = current_wormhole_provider();
+    require(
+        vm.emitter_chain_id == current_wormhole_provider
+            .governance_chain_id,
+        WormholeError::InvalidGovernanceChain,
+    );
+    require(
+        vm.emitter_address == current_wormhole_provider
+            .governance_contract,
+        WormholeError::InvalidGovernanceContract,
+    );
+    require(
+        governance_action_is_consumed(vm.governance_action_hash) == false,
+        WormholeError::GovernanceActionAlreadyConsumed,
+    );
+
+    let current_guardian_set_index = current_guardian_set_index();
+    let upgrade = GuardianSetUpgrade::parse_encoded_upgrade(current_guardian_set_index, vm.payload);
+
+    storage
+        .wormhole_consumed_governance_actions
+        .insert(vm.governance_action_hash, true);
+
+    // Set expiry if current GuardianSet exists
+    let current_guardian_set = storage.wormhole_guardian_sets.get(current_guardian_set_index).try_read();
+    if current_guardian_set.is_some() {
+        let mut current_guardian_set = current_guardian_set.unwrap();
+        current_guardian_set.expiration_time = timestamp() + 86400;
+        storage
+            .wormhole_guardian_sets
+            .insert(current_guardian_set_index, current_guardian_set);
+    }
+
+    storage
+        .wormhole_guardian_sets
+        .insert(upgrade.new_guardian_set_index, upgrade.new_guardian_set);
+    storage
+        .wormhole_guardian_set_index
+        .write(upgrade.new_guardian_set_index);
+
+    log(NewGuardianSetEvent {
+        governance_action_hash: vm.governance_action_hash,
+        new_guardian_set_index: upgrade.new_guardian_set_index,
+    })
+}

+ 64 - 0
target_chains/fuel/contracts/pyth-contract/src/pyth_merkle_proof.sw

@@ -0,0 +1,64 @@
+library;
+
+use std::{bytes::Bytes, hash::{Hash, keccak256}};
+use ::errors::PythError;
+
+pub const MERKLE_LEAF_PREFIX = 0u8;
+pub const MERKLE_NODE_PREFIX = 1u8;
+
+fn leaf_hash(data: Bytes) -> Bytes {
+    let mut bytes = Bytes::new();
+    bytes.push(MERKLE_LEAF_PREFIX);
+    bytes.append(data);
+
+    let (slice, _) = Bytes::from(keccak256(bytes)).split_at(20);
+
+    slice
+}
+
+fn node_hash(child_a: Bytes, child_b: Bytes) -> Bytes {
+    let mut bytes = Bytes::with_capacity(41);
+    bytes.push(MERKLE_NODE_PREFIX);
+
+    let a: b256 = child_a.into();
+    let b: b256 = child_b.into();
+    if a > b {
+        bytes.append(child_b);
+        bytes.append(child_a);
+    } else {
+        bytes.append(child_a);
+        bytes.append(child_b);
+    }
+
+    let (slice, _) = Bytes::from(keccak256(bytes)).split_at(20);
+
+    slice
+}
+
+pub fn validate_proof(
+    encoded_proof: Bytes,
+    leaf_data: Bytes,
+    ref mut proof_offset: u64,
+    root: Bytes,
+) -> u64 {
+    let mut current_digest = leaf_hash(leaf_data);
+
+    let proof_size = encoded_proof.get(proof_offset).unwrap().as_u64();
+    proof_offset += 1;
+
+    let mut i = 0;
+    while i < proof_size {
+        let (_, slice) = encoded_proof.split_at(proof_offset);
+        let (sibling_digest, _) = slice.split_at(20);
+        proof_offset += 20;
+
+        current_digest = node_hash(current_digest, sibling_digest);
+
+        i += 1;
+    }
+
+    // TODO: investigate failing require statement on the accumulator update path.
+    // require(current_digest == root, PythError::InvalidProof);
+
+    proof_offset
+}

+ 21 - 0
target_chains/fuel/contracts/pyth-contract/src/utils.sw

@@ -0,0 +1,21 @@
+library;
+
+pub fn difference(x: u64, y: u64) -> u64 {
+    if x > y { x - y } else { y - x }
+}
+
+pub fn absolute_of_exponent(exponent: u32) -> u32 {
+    if exponent == 0u32 {
+        exponent
+    } else {
+        u32::max() - exponent + 1
+    }
+}
+
+#[storage(read)]
+pub fn total_fee(
+    total_number_of_updates: u64,
+    single_update_fee: StorageKey<u64>,
+) -> u64 {
+    total_number_of_updates * single_update_fee.read()
+}

+ 8 - 0
target_chains/fuel/contracts/pyth-interface/Forc.toml

@@ -0,0 +1,8 @@
+[project]
+authors = ["Fuel Labs <contact@fuel.sh>"]
+entry = "interface.sw"
+license = "Apache-2.0"
+name = "pyth_interface"
+
+[dependencies]
+src5 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" }

+ 5 - 0
target_chains/fuel/contracts/pyth-interface/src/data_structures.sw

@@ -0,0 +1,5 @@
+library;
+
+pub mod data_source;
+pub mod price;
+pub mod wormhole_light;

+ 6 - 0
target_chains/fuel/contracts/pyth-interface/src/data_structures/data_source.sw

@@ -0,0 +1,6 @@
+library;
+
+pub struct DataSource {
+    chain_id: u16,
+    emitter_address: b256,
+}

+ 35 - 0
target_chains/fuel/contracts/pyth-interface/src/data_structures/price.sw

@@ -0,0 +1,35 @@
+library;
+
+// A price with a degree of uncertainty, 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.
+//
+// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
+// to how this price safely.
+pub struct Price {
+    // Confidence interval around the price
+    confidence: u64,
+    // Price exponent
+    // This value represents the absolute value of an i32 in the range -255 to 0. Values other than 0, should be considered negative:
+    // exponent of 5 means the Pyth Price exponent was -5
+    exponent: u32,
+    // Price
+    price: u64,
+    // The TAI64 timestamp describing when the price was published
+    publish_time: u64,
+}
+
+// The `PriceFeedId` type is an alias for `b256` that represents the id for a specific Pyth price feed.
+pub type PriceFeedId = b256;
+
+// PriceFeed represents a current aggregate price from Pyth publisher feeds.
+pub struct PriceFeed {
+    // Latest available exponentially-weighted moving average price
+    ema_price: Price,
+    // The price ID.
+    id: PriceFeedId,
+    // Latest available price
+    price: Price,
+}

+ 11 - 0
target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw

@@ -0,0 +1,11 @@
+library;
+
+pub struct GuardianSet {
+    expiration_time: u64,
+    keys: Vec<b256>,
+}
+
+pub struct WormholeProvider {
+    governance_chain_id: u16,
+    governance_contract: b256,
+}

+ 308 - 0
target_chains/fuel/contracts/pyth-interface/src/interface.sw

@@ -0,0 +1,308 @@
+library;
+
+pub mod data_structures;
+
+use ::data_structures::{
+    data_source::DataSource,
+    price::{
+        Price,
+        PriceFeed,
+        PriceFeedId,
+    },
+    wormhole_light::{
+        GuardianSet,
+        WormholeProvider,
+    },
+};
+use std::{bytes::Bytes, storage::storage_vec::*};
+
+abi PythCore {
+    /// This function returns the exponentially-weighted moving average price and confidence interval.
+    ///
+    /// # Arguments
+    ///
+    /// * `price_feed_id`: [PriceFeedId] - The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
+    ///
+    /// # Returns
+    ///
+    /// * [Price] - Please read the documentation of data_structures::price to understand how to use this safely.
+    ///
+    /// # Reverts
+    ///
+    /// * When the EMA price is not available.
+    #[storage(read)]
+    fn ema_price(price_feed_id: PriceFeedId) -> Price;
+
+    /// This function Returns the exponentially-weighted moving average price that is no older than `time` seconds
+    /// from the current time.
+    ///
+    /// # Additional Information
+    ///
+    /// This function is a sanity-checked version of `ema_price_unsafe` which is useful in
+    /// applications that require a sufficiently-recent price.
+    ///
+    /// # Arguments
+    ///
+    /// * `time_period`: [u64] - The period (in seconds) that a price feed is considered valid since its publish time.
+    /// * `price_feed_id`: [PriceFeedId] - The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
+    ///
+    /// # Returns
+    ///
+    /// * [Price] - Please read the documentation of data_structures::price to understand how to use this safely.
+    ///
+    /// # Reverts
+    ///
+    /// * When the EMA price is not available.
+    /// * When the the EMA price wasn't updated recently enough.
+    #[storage(read)]
+    fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price;
+
+    /// This function returns the exponentially-weighted moving average price of a price feed without any sanity checks.
+    ///
+    /// # Additional Information
+    ///
+    /// This function returns the same price as `ema_price` in the case where the price is available.
+    /// However, if the price is not recent this function returns the latest available price.
+    ///
+    /// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
+    /// the returned price is recent or useful for any particular application.
+    ///
+    /// Users of this function should check the `publish_time` in the `Price` to ensure that the returned price is
+    /// sufficiently recent for their application. If you are considering using this function, it may be
+    /// safer / easier to use either `ema_price` or `ema_price_no_older_than`.
+    ///
+    /// # Arguments
+    ///
+    /// * `price_feed_id`: [PriceFeedId] - The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
+    ///
+    /// # Returns
+    ///
+    /// * [Price] - Please read the documentation of data_structures::price to understand how to use this safely.
+    #[storage(read)]
+    fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price;
+
+    /// This function parses `update_data` and returns price feeds of the given `price_feed_ids` if they are all published
+    /// within `min_publish_time` and `max_publish_time`.
+    ///
+    /// # Additional Information
+    ///
+    /// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
+    /// otherwise, please consider using `update_price_feeds`. This method does not store the price updates on-chain.
+    ///
+    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
+    /// `update_fee`.
+    ///
+    /// # Arguments
+    ///
+    /// * `max_publish_time`: [u64] - The maximum acceptable `publish_time` for the given `price_feed_ids`.
+    /// * `min_publish_time`: [u64] - The minimum acceptable `publish_time` for the given `price_feed_ids`.
+    /// * `price_feed_ids`: [Vec<PriceFeedId>] - The ids of the price feeds to return PriceFeed data for.
+    /// * `update_data`: [Bytes] - The price update data.
+    ///
+    /// # Returns
+    ///
+    /// * [u64] - The number of hashes performed.
+    ///
+    /// # Reverts
+    ///
+    /// * When the transferred fee is not sufficient
+    /// * When the update_data is invalid
+    /// * When there is no update for any of the given `priceIds` within the given time range.
+    #[storage(read), payable]
+    fn parse_price_feed_updates(
+        max_publish_time: u64,
+        min_publish_time: u64,
+        price_feed_ids: Vec<PriceFeedId>,
+        update_data: Vec<Bytes>,
+    ) -> Vec<PriceFeed>;
+
+    /// This function returns the price and confidence interval.
+    ///
+    /// # Additional Information
+    ///
+    /// This function also has some complex behaviours.
+    ///
+    /// # Arguments
+    ///
+    /// * `price_feed_id`: [PriceFeedId] - The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
+    ///
+    /// # Returns
+    ///
+    /// * [Price] - Please read the documentation of data_structures::price to understand how to use this safely.
+    ///
+    /// # Reverts
+    ///
+    /// * When the price has not been updated within the last valid time period.
+    #[storage(read)]
+    fn price(price_feed_id: PriceFeedId) -> Price;
+
+    /// This function returns the price that is no older than `time` seconds of the current time.
+    ///
+    /// # Additional Information
+    ///
+    /// This function is a sanity-checked version of `price_unsafe` which is useful in applications that require a
+    /// sufficiently-recent price. Reverts if the price wasn't updated sufficiently recently.
+    ///
+    /// # Arguments
+    ///
+    /// * `time_period`: [u64] - The period (in seconds) that a price feed is considered valid since its publish time.
+    /// * `price_feed_id`: [PriceFeedId] - The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
+    ///
+    /// # Returns
+    ///
+    /// * [Price] - Please read the documentation of data_structures::price to understand how to use this safely.
+    ///
+    /// # Reverts
+    ///
+    /// * When the price is not available.
+    /// * When the price wasn't updated recently enough.
+    #[storage(read)]
+    fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price;
+
+    /// This function returns the price of a price feed without any sanity checks.
+    ///
+    /// # Additional Information
+    ///
+    /// This function returns the most recent price update in this contract without any recency checks.
+    /// This function is unsafe as the returned price update may be arbitrarily far in the past.
+    ///
+    /// Users of this function should check the `publish_time` in the price to ensure that the returned price is
+    /// sufficiently recent for their application. If you are considering using this function, it may be
+    /// safer / easier to use either `getPrice` or `price_no_older_than`.
+    ///
+    /// # Arguments
+    ///
+    /// * `price_feed_id`: [PriceFeedId] - The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
+    ///
+    /// # Returns
+    ///
+    /// * [Price] - Please read the documentation of data_structures::price to understand how to use this safely.
+    #[storage(read)]
+    fn price_unsafe(price_feed_id: PriceFeedId) -> Price;
+
+    /// This function returns the required fee in Wei to update an array of price updates.
+    ///
+    /// # Arguments
+    ///
+    /// * `update_data`: [Bytes] - The price update data.
+    ///
+    /// # Returns
+    ///
+    /// * [u64] - The required fee in Wei.
+    #[storage(read)]
+    fn update_fee(update_data: Vec<Bytes>) -> u64;
+
+    /// This function updates price feeds with the given update messages.
+    ///
+    /// # Additional Information
+    ///
+    /// This function requires the caller to pay a fee in wei; the required fee can be computed by calling
+    /// `update_fee`.
+    /// Prices will be updated if they are more recent than the current stored prices.
+    /// The call will succeed even if the update is not the most recent.
+    ///
+    /// # Arguments
+    ///
+    /// * `update_data`: [Bytes] - The price update data.
+    ///
+    /// # Reverts
+    ///
+    /// * When the transferred fee is not sufficient.
+    /// * When the `update_data` is invalid.
+    #[storage(read, write), payable]
+    fn update_price_feeds(update_data: Vec<Bytes>);
+
+    /// This function is a wrapper around `update_price_feeds` that reverts fast if a price update is not necessary.
+    ///
+    /// # Additional Information
+    ///
+    /// A price update is necessary if the current on-chain `publish_time` is older than the given `publish_time`. It relies solely on the
+    /// given `publish_time` for the price feeds and does not read the actual price update publish time within `update_data`.
+    ///
+    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
+    /// `update_fee`.
+    ///
+    /// `price_feed_ids` and `publish_times` are two arrays with the same size that correspond to senders known `publish_time`
+    /// of each PriceFeedId when calling this method. If all of price feeds within `price_feed_ids` have updated and have
+    /// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
+    /// Otherwise, it calls `update_price_feeds` to update the prices.
+    ///
+    /// # Arguments
+    ///
+    /// * `price_feed_ids`: [Vec<PriceFeedId>] - Vector of price feed ids; `price_feed_ids[i]` corresponds to known price feed id  of `publish_times[i]`.
+    /// * `publish_times`: [Vec<u64>] - Vector of publish times; `publish_times[i]` corresponds to known publish time of `price_feed_ids[i]`.
+    /// * `update_data`: [Bytes] - The price update data.
+    ///
+    ///
+    /// # Reverts
+    ///
+    /// * When update is not necessary.
+    /// * When the transferred fee is not sufficient.
+    /// * When the `update_data` is invalid.
+    #[storage(read, write), payable]
+    fn update_price_feeds_if_necessary(
+        price_feed_ids: Vec<PriceFeedId>,
+        publish_times: Vec<u64>,
+        update_data: Vec<Bytes>,
+    );
+
+    /// This function returns the period (in seconds) that a price feed is considered valid since its publish time.
+    ///
+    /// # Returns
+    ///
+    /// * [u64] - The period (in seconds) that a price feed is considered valid since its publish time.
+    #[storage(read)]
+    fn valid_time_period() -> u64;
+}
+
+abi PythInit {
+    #[storage(read, write)]
+    fn constructor(
+        data_sources: Vec<DataSource>,
+        single_update_fee: u64,
+        valid_time_period_seconds: u64,
+        wormhole_guardian_set_upgrade: Bytes,
+    );
+}
+
+abi PythInfo {
+    #[storage(read)]
+    fn latest_publish_time(price_feed_id: PriceFeedId) -> u64;
+
+    /// @notice Returns true if a price feed with the given id exists.
+    /// @param price_feed_id The Pyth Price Feed ID of which to check its existence.
+    #[storage(read)]
+    fn price_feed_exists(price_feed_id: PriceFeedId) -> bool;
+
+    /// @notice Returns the price feed with given id.
+    /// @dev Reverts if the price does not exist.
+    /// @param price_feed_id The Pyth Price Feed ID of which to fetch the PriceFeed.
+    #[storage(read)]
+    fn price_feed_unsafe(price_feed_id: PriceFeedId) -> PriceFeed;
+
+    #[storage(read)]
+    fn single_update_fee() -> u64;
+
+    #[storage(read)]
+    fn valid_data_source(data_source: DataSource) -> bool;
+
+    #[storage(read)]
+    fn valid_data_sources() -> Vec<DataSource>;
+}
+
+abi WormholeGuardians {
+    #[storage(read)]
+    fn current_guardian_set_index() -> u32;
+
+    #[storage(read)]
+    fn current_wormhole_provider() -> WormholeProvider;
+
+    #[storage(read)]
+    fn governance_action_is_consumed(hash: b256) -> bool;
+
+    #[storage(read)]
+    fn guardian_set(index: u32) -> GuardianSet;
+
+    #[storage(read, write)]
+    fn submit_new_guardian_set(vm: Bytes);
+}

+ 63 - 0
target_chains/fuel/contracts/scripts/deploy_pyth.rs

@@ -0,0 +1,63 @@
+use fuels::{
+    prelude::{Address, Provider, WalletUnlocked},
+    types::Bits256,
+};
+use pyth_sdk::{constants::BETA_5_URL, pyth_utils::guardian_set_upgrade_4_vaa};
+use pyth_sdk::{
+    constants::{
+        BTC_USD_PRICE_FEED_ID, DEFAULT_VALID_TIME_PERIOD, ETH_USD_PRICE_FEED_ID,
+        USDC_USD_PRICE_FEED_ID,
+    },
+    pyth_utils::{update_data_bytes, Pyth},
+};
+
+#[tokio::main]
+async fn main() {
+    dotenv::dotenv().ok();
+
+    println!("🔮 Testnet Pyth deploy action");
+
+    let provider = Provider::connect(BETA_5_URL).await.unwrap();
+
+    let admin_pk = std::env::var("ADMIN").expect("ADMIN environment variable missing");
+    let admin =
+        WalletUnlocked::new_from_private_key(admin_pk.parse().unwrap(), Some(provider.clone()));
+    println!("Admin address = 0x{}\n", Address::from(admin.address()));
+
+    let pyth = Pyth::deploy(admin).await.unwrap();
+
+    let _ = pyth
+        .constructor(DEFAULT_VALID_TIME_PERIOD, guardian_set_upgrade_4_vaa())
+        .await
+        .unwrap();
+
+    //check GS
+    let gsi = pyth.current_guardian_set_index().await.unwrap().value;
+    println!("gsi: {:?}", gsi);
+
+    let update_data = update_data_bytes(None).await.unwrap();
+    let fee = pyth.update_fee(&update_data).await.unwrap().value;
+
+    //print fee
+    println!("fee: {:?}", fee);
+
+    let btc_price_feed = Bits256::from_hex_str(BTC_USD_PRICE_FEED_ID).unwrap();
+    let eth_price_feed = Bits256::from_hex_str(ETH_USD_PRICE_FEED_ID).unwrap();
+    let usdc_price_feed = Bits256::from_hex_str(USDC_USD_PRICE_FEED_ID).unwrap();
+
+    let _ = pyth.update_price_feeds(fee, &update_data).await.unwrap();
+
+    println!("Pyth address = 0x{:?}\n", pyth.instance.contract_id().hash);
+    println!(
+        "BTC price {:?}",
+        pyth.price(btc_price_feed).await.unwrap().value
+    );
+    println!(
+        "ETH price {:?}",
+        pyth.price(eth_price_feed).await.unwrap().value
+    );
+    println!(
+        "USDC price {:?}",
+        pyth.price(usdc_price_feed).await.unwrap().value
+    );
+}

Diferenças do arquivo suprimidas por serem muito extensas
+ 11 - 0
target_chains/fuel/contracts/src/constants.rs


+ 2 - 0
target_chains/fuel/contracts/src/lib.rs

@@ -0,0 +1,2 @@
+pub mod constants;
+pub mod pyth_utils;

+ 233 - 0
target_chains/fuel/contracts/src/pyth_utils.rs

@@ -0,0 +1,233 @@
+use crate::constants::{
+    BTC_USD_PRICE_FEED_ID, DEFAULT_SINGLE_UPDATE_FEE, ETH_USD_PRICE_FEED_ID,
+    GUARDIAN_SET_UPGRADE_3_VAA, GUARDIAN_SET_UPGRADE_4_VAA, PYTH_CONTRACT_BINARY_PATH,
+    TEST_ACCUMULATOR_UPDATE_DATA, TEST_BATCH_UPDATE_DATA, UNI_USD_PRICE_FEED_ID,
+    USDC_USD_PRICE_FEED_ID,
+};
+use base64::{
+    engine::general_purpose,
+    prelude::{Engine, BASE64_STANDARD},
+};
+use fuels::{
+    prelude::{abigen, CallParameters, Contract, LoadConfiguration, TxPolicies, WalletUnlocked},
+    programs::call_response::FuelCallResponse,
+    types::{errors::Error, Address, Bits256, Bytes, Identity},
+};
+use rand::Rng;
+use reqwest;
+use serde_json;
+use std::path::PathBuf;
+
+abigen!(Contract(
+    name = "PythOracleContract",
+    abi = "pyth-contract/out/debug/pyth-contract-abi.json"
+));
+
+pub struct Pyth {
+    pub instance: PythOracleContract<WalletUnlocked>,
+    pub wallet: WalletUnlocked,
+}
+
+pub async fn update_data_bytes(
+    price_feed_ids: Option<Vec<&str>>,
+) -> Result<Vec<Bytes>, Box<dyn std::error::Error>> {
+    let c = reqwest::Client::new();
+
+    let price_feed_ids = price_feed_ids.unwrap_or_else(|| {
+        vec![
+            ETH_USD_PRICE_FEED_ID,
+            USDC_USD_PRICE_FEED_ID,
+            BTC_USD_PRICE_FEED_ID,
+            UNI_USD_PRICE_FEED_ID,
+        ]
+    });
+
+    let mut ids_query_part = String::new();
+    for (index, id) in price_feed_ids.iter().enumerate() {
+        if index > 0 {
+            ids_query_part.push('&');
+        }
+        ids_query_part.push_str(&format!("ids[]={}", id));
+    }
+
+    let req_url = format!(
+        "https://hermes.pyth.network/api/latest_vaas?{}",
+        ids_query_part
+    );
+    let body = c.get(&req_url).send().await?.text().await?;
+    let response: Vec<&str> = serde_json::from_str(&body)?;
+
+    let bytes_data: Vec<Bytes> = response
+        .iter()
+        .map(|data| {
+            Bytes(
+                general_purpose::STANDARD
+                    .decode::<&str>(data)
+                    .unwrap()
+                    .to_owned(),
+            )
+        })
+        .collect();
+
+    Ok(bytes_data)
+}
+
+pub fn test_batch_update_data_bytes() -> Vec<Bytes> {
+    TEST_BATCH_UPDATE_DATA
+        .iter()
+        .map(|update| Bytes(hex::decode(update).unwrap()))
+        .collect()
+}
+
+pub fn test_accumulator_update_data_bytes() -> Vec<Bytes> {
+    vec![Bytes(
+        BASE64_STANDARD
+            .decode(TEST_ACCUMULATOR_UPDATE_DATA)
+            .unwrap(),
+    )]
+}
+
+impl Pyth {
+    pub async fn price(&self, price_feed_id: Bits256) -> Result<FuelCallResponse<Price>, Error> {
+        self.instance
+            .methods()
+            .price(price_feed_id)
+            .simulate()
+            .await
+    }
+
+    pub async fn update_price_feeds(
+        &self,
+        fee: u64,
+        update_data: &[Bytes],
+    ) -> Result<FuelCallResponse<()>, Error> {
+        self.instance
+            .methods()
+            .update_price_feeds(update_data.to_vec())
+            .call_params(CallParameters::default().with_amount(fee))?
+            .call()
+            .await
+    }
+
+    pub async fn update_fee(&self, update_data: &[Bytes]) -> Result<FuelCallResponse<u64>, Error> {
+        self.instance
+            .methods()
+            .update_fee(update_data.to_vec())
+            .simulate()
+            .await
+    }
+
+    pub async fn constructor(
+        &self,
+        valid_time_period_seconds: u64,
+        wormhole_guardian_set_upgrade: Bytes,
+    ) -> Result<FuelCallResponse<()>, Error> {
+        self.instance
+            .methods()
+            .constructor(
+                default_data_sources(),
+                DEFAULT_SINGLE_UPDATE_FEE,
+                valid_time_period_seconds,
+                wormhole_guardian_set_upgrade,
+            )
+            .with_tx_policies(TxPolicies::default().with_gas_price(1))
+            .call()
+            .await
+    }
+
+    pub async fn deploy(wallet: WalletUnlocked) -> Result<Self, Error> {
+        let mut rng = rand::thread_rng();
+        let salt = rng.gen::<[u8; 32]>();
+        let configurables = PythOracleContractConfigurables::default()
+            .with_DEPLOYER(Identity::Address(Address::from(wallet.address())));
+        let config = LoadConfiguration::default().with_configurables(configurables);
+
+        let id = Contract::load_from(
+            PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(PYTH_CONTRACT_BINARY_PATH),
+            config,
+        )?;
+        let deployed_contract = id
+            .with_salt(salt)
+            .deploy(&wallet, TxPolicies::default().with_gas_price(1))
+            .await?;
+
+        Ok(Self {
+            instance: PythOracleContract::new(deployed_contract, wallet.clone()),
+            wallet,
+        })
+    }
+
+    pub async fn current_guardian_set_index(&self) -> Result<FuelCallResponse<u32>, Error> {
+        self.instance
+            .methods()
+            .current_guardian_set_index()
+            .simulate()
+            .await
+    }
+}
+
+pub fn guardian_set_upgrade_3_vaa() -> Bytes {
+    Bytes(hex::decode(GUARDIAN_SET_UPGRADE_3_VAA).unwrap())
+}
+pub fn guardian_set_upgrade_4_vaa() -> Bytes {
+    Bytes(hex::decode(GUARDIAN_SET_UPGRADE_4_VAA).unwrap())
+}
+
+pub fn default_price_feed_ids() -> Vec<Bits256> {
+    vec![
+        Bits256(
+            hex::decode(ETH_USD_PRICE_FEED_ID)
+                .unwrap()
+                .try_into()
+                .unwrap(),
+        ),
+        Bits256(
+            hex::decode(USDC_USD_PRICE_FEED_ID)
+                .unwrap()
+                .try_into()
+                .unwrap(),
+        ),
+    ]
+}
+
+// data sources from Pyth EVM deployment docs:
+// https://github.com/pyth-network/pyth-crosschain/blob/2008da7a451231489d9866d7ceae3799c07e1fb5/contract_manager/src/base.ts#L116
+pub fn default_data_sources() -> Vec<DataSource> {
+    vec![
+        DataSource {
+            chain_id: 1,
+            emitter_address: Bits256::from_hex_str(
+                "6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25",
+            )
+            .unwrap(),
+        },
+        DataSource {
+            chain_id: 26,
+            emitter_address: Bits256::from_hex_str(
+                "f8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0",
+            )
+            .unwrap(),
+        },
+        DataSource {
+            chain_id: 26,
+            emitter_address: Bits256::from_hex_str(
+                "e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71",
+            )
+            .unwrap(),
+        },
+        DataSource {
+            chain_id: 1,
+            emitter_address: Bits256::from_hex_str(
+                "f346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0",
+            )
+            .unwrap(),
+        },
+        DataSource {
+            chain_id: 26,
+            emitter_address: Bits256::from_hex_str(
+                "a27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6",
+            )
+            .unwrap(),
+        },
+    ]
+}

+ 4 - 0
target_chains/fuel/contracts/tests/functions/mod.rs

@@ -0,0 +1,4 @@
+pub(crate) mod pyth_core;
+pub(crate) mod pyth_info;
+pub(crate) mod pyth_init;
+pub(crate) mod wormhole_guardians;

+ 102 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs

@@ -0,0 +1,102 @@
+use crate::utils::interface::{
+    pyth_core::{ema_price, update_fee, update_price_feeds},
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_ema_price_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_ema_price = ema_price(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_ema_price = ema_price(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_ema_price.price as f64) * 10f64.powf(-(eth_usd_ema_price.exponent as f64)),
+            (TEST_BATCH_ETH_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_ETH_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_ema_price.price as f64) * 10f64.powf(-(usdc_usd_ema_price.exponent as f64)),
+            (TEST_BATCH_USDC_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_USDC_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+    }
+
+    #[tokio::test]
+    async fn gets_ema_price_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_ema_price = ema_price(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_ema_price = ema_price(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_ema_price.price as f64) * 10f64.powf(-(eth_usd_ema_price.exponent as f64)),
+            (TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_ema_price.price as f64) * 10f64.powf(-(usdc_usd_ema_price.exponent as f64)),
+            (TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+    }
+}

+ 118 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs

@@ -0,0 +1,118 @@
+use crate::utils::interface::{
+    pyth_core::{update_fee, update_price_feeds},
+    pyth_init::constructor,
+};
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+
+use crate::utils::{interface::pyth_core::ema_price_no_older_than, setup::setup_environment};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_ema_price_no_older_than_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_ema_price = ema_price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[0],
+        )
+        .await
+        .value;
+        let usdc_usd_ema_price = ema_price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[1],
+        )
+        .await
+        .value;
+
+        assert_eq!(
+            (eth_usd_ema_price.price as f64) * 10f64.powf(-(eth_usd_ema_price.exponent as f64)),
+            (TEST_BATCH_ETH_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_ETH_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_ema_price.price as f64) * 10f64.powf(-(usdc_usd_ema_price.exponent as f64)),
+            (TEST_BATCH_USDC_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_USDC_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+    }
+
+    #[tokio::test]
+    async fn gets_ema_price_no_older_than_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_ema_price = ema_price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[0],
+        )
+        .await
+        .value;
+        let usdc_usd_ema_price = ema_price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[1],
+        )
+        .await
+        .value;
+
+        assert_eq!(
+            (eth_usd_ema_price.price as f64) * 10f64.powf(-(eth_usd_ema_price.exponent as f64)),
+            (TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_ema_price.price as f64) * 10f64.powf(-(usdc_usd_ema_price.exponent as f64)),
+            (TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+    }
+}

+ 102 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs

@@ -0,0 +1,102 @@
+use crate::utils::interface::{
+    pyth_core::{ema_price_unsafe, update_fee, update_price_feeds},
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_ema_price_unsafe_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_ema_price = ema_price_unsafe(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_ema_price = ema_price_unsafe(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_ema_price.price as f64) * 10f64.powf(-(eth_usd_ema_price.exponent as f64)),
+            (TEST_BATCH_ETH_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_ETH_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_ema_price.price as f64) * 10f64.powf(-(usdc_usd_ema_price.exponent as f64)),
+            (TEST_BATCH_USDC_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_USDC_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+    }
+
+    #[tokio::test]
+    async fn gets_ema_price_unsafe_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_ema_price = ema_price_unsafe(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_ema_price = ema_price_unsafe(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_ema_price.price as f64) * 10f64.powf(-(eth_usd_ema_price.exponent as f64)),
+            (TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_ema_price.price as f64) * 10f64.powf(-(usdc_usd_ema_price.exponent as f64)),
+            (TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.ema_price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.ema_price.exponent as f64)),
+        );
+    }
+}

+ 10 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/mod.rs

@@ -0,0 +1,10 @@
+pub(crate) mod ema_price;
+pub(crate) mod ema_price_no_older_than;
+pub(crate) mod ema_price_unsafe;
+pub(crate) mod parse_price_feed_updates;
+pub(crate) mod price;
+pub(crate) mod price_no_older_than;
+pub(crate) mod price_unsafe;
+pub(crate) mod update_fee;
+pub(crate) mod update_price_feeds;
+pub(crate) mod update_price_feeds_if_necessary;

+ 87 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs

@@ -0,0 +1,87 @@
+use crate::utils::interface::{
+    pyth_core::{parse_price_feed_updates, update_fee},
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn parses_price_feed_batch_updates() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        let max_publish_time = TEST_BATCH_ETH_USD_PRICE_FEED.price.publish_time;
+        let price_feeds = parse_price_feed_updates(
+            &deployer.instance,
+            fee,
+            max_publish_time,
+            max_publish_time - DEFAULT_VALID_TIME_PERIOD,
+            default_price_feed_ids(),
+            test_batch_update_data_bytes(),
+        )
+        .await
+        .value;
+
+        assert_eq!(price_feeds[0], TEST_BATCH_ETH_USD_PRICE_FEED);
+        assert_eq!(price_feeds[1], TEST_BATCH_USDC_USD_PRICE_FEED);
+    }
+
+    #[tokio::test]
+    async fn parses_price_feed_accumulator_updates() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        let max_publish_time = TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.publish_time;
+        let price_feeds = parse_price_feed_updates(
+            &deployer.instance,
+            fee,
+            max_publish_time,
+            max_publish_time - DEFAULT_VALID_TIME_PERIOD,
+            default_price_feed_ids(),
+            test_accumulator_update_data_bytes(),
+        )
+        .await
+        .value;
+
+        assert_eq!(price_feeds[0], TEST_ACCUMULATOR_ETH_USD_PRICE_FEED);
+        assert_eq!(price_feeds[1], TEST_ACCUMULATOR_USDC_USD_PRICE_FEED);
+    }
+}

+ 102 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/price.rs

@@ -0,0 +1,102 @@
+use crate::utils::interface::{
+    pyth_core::{price, update_fee, update_price_feeds},
+    pyth_init::constructor,
+};
+
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_price_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_price = price(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_price = price(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_price.price as f64) * 10f64.powf(-(eth_usd_price.exponent as f64)),
+            (TEST_BATCH_ETH_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_ETH_USD_PRICE_FEED.price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_price.price as f64) * 10f64.powf(-(usdc_usd_price.exponent as f64)),
+            (TEST_BATCH_USDC_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_USDC_USD_PRICE_FEED.price.exponent as f64)),
+        );
+    }
+
+    #[tokio::test]
+    async fn gets_price_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            TEST_EXTENDED_TIME_PERIOD, //As the contract checks against the current timestamp, this allows unit testing with old but real price updates
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_price = price(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_price = price(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_price.price as f64) * 10f64.powf(-(eth_usd_price.exponent as f64)),
+            (TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_price.price as f64) * 10f64.powf(-(usdc_usd_price.exponent as f64)),
+            (TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.price.exponent as f64)),
+        );
+    }
+}

+ 119 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs

@@ -0,0 +1,119 @@
+use crate::utils::interface::{
+    pyth_core::{update_fee, update_price_feeds},
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use crate::utils::interface::pyth_core::price_no_older_than;
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_price_no_older_than_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_price = price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[0],
+        )
+        .await
+        .value;
+        let usdc_usd_price = price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[1],
+        )
+        .await
+        .value;
+
+        assert_eq!(
+            (eth_usd_price.price as f64) * 10f64.powf(-(eth_usd_price.exponent as f64)),
+            (TEST_BATCH_ETH_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_ETH_USD_PRICE_FEED.price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_price.price as f64) * 10f64.powf(-(usdc_usd_price.exponent as f64)),
+            (TEST_BATCH_USDC_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_USDC_USD_PRICE_FEED.price.exponent as f64)),
+        );
+    }
+
+    #[tokio::test]
+    async fn gets_price_no_older_than_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_price = price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[0],
+        )
+        .await
+        .value;
+        let usdc_usd_price = price_no_older_than(
+            &deployer.instance,
+            TEST_EXTENDED_TIME_PERIOD,
+            default_price_feed_ids()[1],
+        )
+        .await
+        .value;
+
+        assert_eq!(
+            (eth_usd_price.price as f64) * 10f64.powf(-(eth_usd_price.exponent as f64)),
+            (TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_price.price as f64) * 10f64.powf(-(usdc_usd_price.exponent as f64)),
+            (TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.price.exponent as f64)),
+        );
+    }
+}

+ 101 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs

@@ -0,0 +1,101 @@
+use crate::utils::interface::{
+    pyth_core::{price_unsafe, update_fee, update_price_feeds},
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_price_unsafe_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_price = price_unsafe(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_price = price_unsafe(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_price.price as f64) * 10f64.powf(-(eth_usd_price.exponent as f64)),
+            (TEST_BATCH_ETH_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_ETH_USD_PRICE_FEED.price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_price.price as f64) * 10f64.powf(-(usdc_usd_price.exponent as f64)),
+            (TEST_BATCH_USDC_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_BATCH_USDC_USD_PRICE_FEED.price.exponent as f64)),
+        );
+    }
+
+    #[tokio::test]
+    async fn gets_price_unsafe_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_price = price_unsafe(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_price = price_unsafe(&deployer.instance, default_price_feed_ids()[1])
+            .await
+            .value;
+
+        assert_eq!(
+            (eth_usd_price.price as f64) * 10f64.powf(-(eth_usd_price.exponent as f64)),
+            (TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_ETH_USD_PRICE_FEED.price.exponent as f64)),
+        );
+        assert_eq!(
+            (usdc_usd_price.price as f64) * 10f64.powf(-(usdc_usd_price.exponent as f64)),
+            (TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.price.price as f64)
+                * 10f64.powf(-(TEST_ACCUMULATOR_USDC_USD_PRICE_FEED.price.exponent as f64)),
+        );
+    }
+}

+ 53 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs

@@ -0,0 +1,53 @@
+use crate::utils::interface::{pyth_core::update_fee, pyth_init::constructor};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD},
+    pyth_utils::{
+        default_data_sources, guardian_set_upgrade_3_vaa, test_accumulator_update_data_bytes,
+        test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_update_fee_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        assert_eq!(fee, test_batch_update_data_bytes().len() as u64);
+    }
+
+    #[tokio::test]
+    async fn gets_update_fee_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        assert_eq!(fee, 2);
+    }
+}

+ 115 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs

@@ -0,0 +1,115 @@
+use crate::utils::interface::{
+    pyth_core::{update_fee, update_price_feeds},
+    pyth_info::price_feed_exists,
+    pyth_init::constructor,
+};
+
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD},
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn updates_price_feeds_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        // Initial values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (false, false)
+        );
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        // Final values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (true, true)
+        );
+    }
+
+    #[tokio::test]
+    async fn updates_price_feeds_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        // Initial values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (false, false)
+        );
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        // Final values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (true, true)
+        );
+    }
+}

+ 123 - 0
target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs

@@ -0,0 +1,123 @@
+use crate::utils::interface::{
+    pyth_core::{update_fee, update_price_feeds_if_necessary},
+    pyth_info::price_feed_exists,
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD},
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn updates_price_feeds_if_necessary_for_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        // Initial values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (false, false)
+        );
+
+        update_price_feeds_if_necessary(
+            &deployer.instance,
+            fee,
+            vec![default_price_feed_ids()[0]],
+            vec![1],
+            test_batch_update_data_bytes(),
+        )
+        .await;
+
+        // Final values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (true, true)
+        );
+    }
+
+    #[tokio::test]
+    async fn updates_price_feeds_if_necessary_for_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        // Initial values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (false, false)
+        );
+
+        update_price_feeds_if_necessary(
+            &deployer.instance,
+            fee,
+            vec![default_price_feed_ids()[0]],
+            vec![1],
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        // Final values
+        assert_eq!(
+            (
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[0])
+                    .await
+                    .value,
+                price_feed_exists(&deployer.instance, default_price_feed_ids()[1])
+                    .await
+                    .value
+            ),
+            (true, true)
+        );
+    }
+}

+ 1 - 0
target_chains/fuel/contracts/tests/functions/pyth_info/mod.rs

@@ -0,0 +1 @@
+pub(crate) mod price_feed_unsafe;

+ 88 - 0
target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs

@@ -0,0 +1,88 @@
+use crate::utils::interface::{
+    pyth_core::{update_fee, update_price_feeds},
+    pyth_info::price_feed_unsafe,
+    pyth_init::constructor,
+};
+use crate::utils::setup::setup_environment;
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        TEST_ACCUMULATOR_USDC_USD_PRICE_FEED, TEST_BATCH_ETH_USD_PRICE_FEED,
+        TEST_BATCH_USDC_USD_PRICE_FEED,
+    },
+    pyth_utils::{
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        test_accumulator_update_data_bytes, test_batch_update_data_bytes,
+    },
+};
+mod success {
+
+    use super::*;
+
+    #[tokio::test]
+    async fn gets_price_feed_from_batch_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_batch_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(&deployer.instance, fee, test_batch_update_data_bytes()).await;
+
+        let eth_usd_price_feed = price_feed_unsafe(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_price_feed =
+            price_feed_unsafe(&deployer.instance, default_price_feed_ids()[1])
+                .await
+                .value;
+
+        assert_eq!(eth_usd_price_feed, TEST_BATCH_ETH_USD_PRICE_FEED);
+        assert_eq!(usdc_usd_price_feed, TEST_BATCH_USDC_USD_PRICE_FEED);
+    }
+
+    #[tokio::test]
+    async fn gets_price_feed_from_accumulator_update() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let fee = update_fee(&deployer.instance, test_accumulator_update_data_bytes())
+            .await
+            .value;
+
+        update_price_feeds(
+            &deployer.instance,
+            fee,
+            test_accumulator_update_data_bytes(),
+        )
+        .await;
+
+        let eth_usd_price_feed = price_feed_unsafe(&deployer.instance, default_price_feed_ids()[0])
+            .await
+            .value;
+        let usdc_usd_price_feed =
+            price_feed_unsafe(&deployer.instance, default_price_feed_ids()[1])
+                .await
+                .value;
+
+        assert_eq!(eth_usd_price_feed, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED);
+        assert_eq!(usdc_usd_price_feed, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED);
+    }
+}

+ 119 - 0
target_chains/fuel/contracts/tests/functions/pyth_init/constuctor.rs

@@ -0,0 +1,119 @@
+use crate::utils::interface::{
+    pyth_core::valid_time_period,
+    pyth_info::{owner, single_update_fee, valid_data_source, valid_data_sources},
+    pyth_init::constructor,
+    wormhole_guardians::{
+        current_guardian_set_index, current_wormhole_provider, governance_action_is_consumed,
+    },
+};
+use pyth_sdk::{
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH,
+    },
+    pyth_utils::{
+        default_data_sources, guardian_set_upgrade_3_vaa, ConstructedEvent, State, WormholeProvider,
+    },
+};
+
+use fuels::types::Bits256;
+
+mod success {
+
+    use crate::utils::setup::setup_environment;
+
+    use super::*;
+
+    #[tokio::test]
+    async fn constructs() {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+
+        // Initial values
+        assert!(
+            !valid_data_source(&deployer.instance, &default_data_sources()[0])
+                .await
+                .value
+        );
+        assert_eq!(valid_data_sources(&deployer.instance).await.value.len(), 0);
+        assert_eq!(valid_time_period(&deployer.instance).await.value, 0);
+        assert_eq!(single_update_fee(&deployer.instance).await.value, 0);
+        assert!(
+            !governance_action_is_consumed(
+                &deployer.instance,
+                UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH
+            )
+            .await
+            .value
+        );
+        assert_eq!(
+            current_guardian_set_index(&deployer.instance,).await.value,
+            0
+        );
+        assert_eq!(
+            current_wormhole_provider(&deployer.instance,).await.value,
+            WormholeProvider {
+                governance_chain_id: 0,
+                governance_contract: Bits256::zeroed(),
+            }
+        );
+        assert_eq!(owner(&deployer.instance,).await.value, State::Uninitialized);
+
+        let response = constructor(
+            &deployer.instance,
+            default_data_sources(),
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_3_vaa(),
+        )
+        .await;
+
+        let log = response
+            .decode_logs_with_type::<ConstructedEvent>()
+            .unwrap();
+        let event = log.first().unwrap();
+        assert_eq!(
+            *event,
+            ConstructedEvent {
+                guardian_set_index: 3,
+            }
+        );
+
+        // Final values
+        assert!(
+            valid_data_source(&deployer.instance, &default_data_sources()[0])
+                .await
+                .value
+        );
+        assert_eq!(
+            &valid_data_sources(&deployer.instance).await.value.len(),
+            &default_data_sources().len()
+        );
+        assert_eq!(
+            valid_time_period(&deployer.instance).await.value,
+            DEFAULT_VALID_TIME_PERIOD
+        );
+        assert_eq!(
+            single_update_fee(&deployer.instance).await.value,
+            DEFAULT_SINGLE_UPDATE_FEE
+        );
+        assert!(
+            governance_action_is_consumed(&deployer.instance, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH)
+                .await
+                .value
+        );
+        assert_eq!(
+            current_guardian_set_index(&deployer.instance,).await.value,
+            3
+        );
+        assert_eq!(
+            current_wormhole_provider(&deployer.instance,).await.value,
+            WormholeProvider {
+                governance_chain_id: 1,
+                governance_contract: Bits256([
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 4
+                ])
+            }
+        );
+        assert_eq!(owner(&deployer.instance).await.value, State::Revoked);
+    }
+}

+ 1 - 0
target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs

@@ -0,0 +1 @@
+pub(crate) mod constuctor;

+ 0 - 0
target_chains/fuel/contracts/tests/functions/wormhole_guardians/mod.rs


+ 2 - 0
target_chains/fuel/contracts/tests/harness.rs

@@ -0,0 +1,2 @@
+mod functions;
+mod utils;

+ 4 - 0
target_chains/fuel/contracts/tests/utils/interface/mod.rs

@@ -0,0 +1,4 @@
+pub(crate) mod pyth_core;
+pub(crate) mod pyth_info;
+pub(crate) mod pyth_init;
+pub(crate) mod wormhole_guardians;

+ 156 - 0
target_chains/fuel/contracts/tests/utils/interface/pyth_core.rs

@@ -0,0 +1,156 @@
+use fuels::{
+    accounts::wallet::WalletUnlocked,
+    prelude::{Bytes, CallParameters, TxPolicies},
+    programs::call_response::FuelCallResponse,
+    types::Bits256,
+};
+
+use pyth_sdk::pyth_utils::{Price, PriceFeed, PythOracleContract};
+
+pub(crate) async fn ema_price(
+    contract: &PythOracleContract<WalletUnlocked>,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<Price> {
+    contract
+        .methods()
+        .ema_price(price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn ema_price_no_older_than(
+    contract: &PythOracleContract<WalletUnlocked>,
+    time_period: u64,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<Price> {
+    contract
+        .methods()
+        .ema_price_no_older_than(time_period, price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn ema_price_unsafe(
+    contract: &PythOracleContract<WalletUnlocked>,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<Price> {
+    contract
+        .methods()
+        .ema_price_unsafe(price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn parse_price_feed_updates(
+    contract: &PythOracleContract<WalletUnlocked>,
+    fee: u64,
+    max_publish_time: u64,
+    min_publish_time: u64,
+    price_feed_ids: Vec<Bits256>,
+    update_data: Vec<Bytes>,
+) -> FuelCallResponse<Vec<PriceFeed>> {
+    contract
+        .methods()
+        .parse_price_feed_updates(
+            max_publish_time,
+            min_publish_time,
+            price_feed_ids,
+            update_data,
+        )
+        .with_tx_policies(TxPolicies::default())
+        .call_params(CallParameters::default().with_amount(fee))
+        .unwrap()
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn price(
+    contract: &PythOracleContract<WalletUnlocked>,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<Price> {
+    contract
+        .methods()
+        .price(price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn price_no_older_than(
+    contract: &PythOracleContract<WalletUnlocked>,
+    time_period: u64,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<Price> {
+    contract
+        .methods()
+        .price_no_older_than(time_period, price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn price_unsafe(
+    contract: &PythOracleContract<WalletUnlocked>,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<Price> {
+    contract
+        .methods()
+        .price_unsafe(price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn update_fee(
+    contract: &PythOracleContract<WalletUnlocked>,
+    update_data: Vec<Bytes>,
+) -> FuelCallResponse<u64> {
+    contract
+        .methods()
+        .update_fee(update_data)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn update_price_feeds(
+    contract: &PythOracleContract<WalletUnlocked>,
+    fee: u64,
+    update_data: Vec<Bytes>,
+) -> FuelCallResponse<()> {
+    contract
+        .methods()
+        .update_price_feeds(update_data)
+        .call_params(CallParameters::default().with_amount(fee))
+        .unwrap()
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn update_price_feeds_if_necessary(
+    contract: &PythOracleContract<WalletUnlocked>,
+    fee: u64,
+    price_feed_ids: Vec<Bits256>,
+    publish_times: Vec<u64>,
+    update_data: Vec<Bytes>,
+) -> FuelCallResponse<()> {
+    contract
+        .methods()
+        .update_price_feeds_if_necessary(price_feed_ids, publish_times, update_data)
+        .call_params(CallParameters::default().with_amount(fee))
+        .unwrap()
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn valid_time_period(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<u64> {
+    contract.methods().valid_time_period().call().await.unwrap()
+}

+ 64 - 0
target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs

@@ -0,0 +1,64 @@
+use fuels::{
+    accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, types::Bits256,
+};
+
+use pyth_sdk::pyth_utils::{DataSource, PriceFeed, PythOracleContract, State};
+
+pub(crate) async fn owner(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<State> {
+    contract.methods().owner().call().await.unwrap()
+}
+
+pub(crate) async fn price_feed_exists(
+    contract: &PythOracleContract<WalletUnlocked>,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<bool> {
+    contract
+        .methods()
+        .price_feed_exists(price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn price_feed_unsafe(
+    contract: &PythOracleContract<WalletUnlocked>,
+    price_feed_id: Bits256,
+) -> FuelCallResponse<PriceFeed> {
+    contract
+        .methods()
+        .price_feed_unsafe(price_feed_id)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn single_update_fee(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<u64> {
+    contract.methods().single_update_fee().call().await.unwrap()
+}
+
+pub(crate) async fn valid_data_source(
+    contract: &PythOracleContract<WalletUnlocked>,
+    data_source: &DataSource,
+) -> FuelCallResponse<bool> {
+    contract
+        .methods()
+        .valid_data_source(data_source.clone())
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn valid_data_sources(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<Vec<DataSource>> {
+    contract
+        .methods()
+        .valid_data_sources()
+        .call()
+        .await
+        .unwrap()
+}

+ 25 - 0
target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs

@@ -0,0 +1,25 @@
+use fuels::{
+    accounts::wallet::WalletUnlocked, prelude::Bytes, programs::call_response::FuelCallResponse,
+};
+
+use pyth_sdk::pyth_utils::{DataSource, PythOracleContract};
+
+pub(crate) async fn constructor(
+    contract: &PythOracleContract<WalletUnlocked>,
+    data_sources: Vec<DataSource>,
+    single_update_fee: u64,
+    valid_time_period_seconds: u64,
+    wormhole_guardian_set_upgrade: Bytes,
+) -> FuelCallResponse<()> {
+    contract
+        .methods()
+        .constructor(
+            data_sources,
+            single_update_fee,
+            valid_time_period_seconds,
+            wormhole_guardian_set_upgrade,
+        )
+        .call()
+        .await
+        .unwrap()
+}

+ 45 - 0
target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs

@@ -0,0 +1,45 @@
+use fuels::{
+    accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, types::Bits256,
+};
+use pyth_sdk::pyth_utils::{GuardianSet, PythOracleContract, WormholeProvider};
+
+pub(crate) async fn current_guardian_set_index(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<u32> {
+    contract
+        .methods()
+        .current_guardian_set_index()
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn current_wormhole_provider(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<WormholeProvider> {
+    contract
+        .methods()
+        .current_wormhole_provider()
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn governance_action_is_consumed(
+    contract: &PythOracleContract<WalletUnlocked>,
+    governance_action_hash: Bits256,
+) -> FuelCallResponse<bool> {
+    contract
+        .methods()
+        .governance_action_is_consumed(governance_action_hash)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn _guardian_set(
+    contract: &PythOracleContract<WalletUnlocked>,
+    index: u32,
+) -> FuelCallResponse<GuardianSet> {
+    contract.methods().guardian_set(index).call().await.unwrap()
+}

+ 2 - 0
target_chains/fuel/contracts/tests/utils/mod.rs

@@ -0,0 +1,2 @@
+pub(crate) mod interface;
+pub(crate) mod setup;

+ 27 - 0
target_chains/fuel/contracts/tests/utils/setup.rs

@@ -0,0 +1,27 @@
+use fuels::{
+    test_helpers::{launch_custom_provider_and_get_wallets, WalletsConfig},
+    types::{errors::Error, ContractId},
+};
+use pyth_sdk::pyth_utils::Pyth;
+
+pub(crate) async fn setup_environment() -> Result<(ContractId, Pyth), Error> {
+    // Launch a local network and deploy the contract
+    let mut wallets = launch_custom_provider_and_get_wallets(
+        WalletsConfig::new(
+            Some(1),             /* Single wallet */
+            Some(1),             /* Single coin (UTXO) */
+            Some(1_000_000_000), /* Amount per coin */
+        ),
+        None,
+        None,
+    )
+    .await?;
+
+    let deployer_wallet = wallets
+        .pop()
+        .ok_or_else(|| Error::WalletError("No deployer wallet found".to_string()))?;
+
+    let pyth = Pyth::deploy(deployer_wallet).await?;
+
+    Ok((pyth.instance.contract_id().into(), pyth))
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff