Kaynağa Gözat

Merge pull request #2795 from pyth-network/pyth-stylus-update-and-initialize

feat(stylus) - initialize and update fees for pyth contract
Ayush Suresh 4 ay önce
ebeveyn
işleme
fb52e84f25

+ 599 - 34
target_chains/stylus/Cargo.lock

@@ -38,6 +38,29 @@ dependencies = [
  "cpufeatures",
 ]
 
+[[package]]
+name = "ahash"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
+dependencies = [
+ "getrandom 0.2.16",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if 1.0.0",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
 [[package]]
 name = "aho-corasick"
 version = "1.1.3"
@@ -72,7 +95,7 @@ checksum = "69e32ef5c74bbeb1733c37f4ac7f866f8c8af208b7b4265e21af609dcac5bd5e"
 dependencies = [
  "alloy-eips",
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "alloy-serde",
  "alloy-trie",
  "auto_impl",
@@ -90,7 +113,7 @@ dependencies = [
  "alloy-consensus",
  "alloy-eips",
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "alloy-serde",
  "serde",
 ]
@@ -102,7 +125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "675264c957689f0fd75f5993a73123c2cc3b5c235a38f5b9037fe6c826bfb2c0"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "crc",
  "thiserror 2.0.12",
 ]
@@ -114,7 +137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "serde",
 ]
 
@@ -125,7 +148,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b15b13d38b366d01e818fe8e710d4d702ef7499eacd44926a06171dd9585d0c"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
+ "k256",
  "serde",
  "thiserror 2.0.12",
 ]
@@ -140,7 +164,7 @@ dependencies = [
  "alloy-eip2930",
  "alloy-eip7702",
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "alloy-serde",
  "auto_impl",
  "c-kzg",
@@ -214,6 +238,23 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "alloy-primitives"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f09d3cd21353cf2f41f401e35093390f2a238ce0a72af0f6fc6be5157f525c7"
+dependencies = [
+ "alloy-rlp 0.2.0",
+ "bytes",
+ "const-hex",
+ "derive_more 0.99.20",
+ "itoa",
+ "proptest",
+ "ruint2",
+ "serde",
+ "tiny-keccak",
+]
+
 [[package]]
 name = "alloy-primitives"
 version = "0.7.6"
@@ -236,12 +277,15 @@ version = "0.8.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bc1360603efdfba91151e623f13a4f4d3dc4af4adc1cbd90bf37c81e84db4c77"
 dependencies = [
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
+ "arbitrary",
  "bytes",
  "cfg-if 1.0.0",
  "const-hex",
+ "derive_arbitrary",
  "derive_more 1.0.0",
  "foldhash",
+ "getrandom 0.2.16",
  "hashbrown 0.15.3",
  "indexmap",
  "itoa",
@@ -249,6 +293,7 @@ dependencies = [
  "keccak-asm",
  "paste",
  "proptest",
+ "proptest-derive",
  "rand 0.8.5",
  "ruint",
  "rustc-hash",
@@ -294,6 +339,16 @@ dependencies = [
  "wasmtimer",
 ]
 
+[[package]]
+name = "alloy-rlp"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aa8b63682a5fa571b7c0281839e267b6a1f7569ba07b9b57b093050b7c2efda"
+dependencies = [
+ "arrayvec",
+ "bytes",
+]
+
 [[package]]
 name = "alloy-rlp"
 version = "0.3.12"
@@ -361,7 +416,7 @@ dependencies = [
  "alloy-eips",
  "alloy-network-primitives",
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "alloy-serde",
  "alloy-sol-types 0.8.20",
  "itertools 0.14.0",
@@ -396,6 +451,22 @@ dependencies = [
  "thiserror 2.0.12",
 ]
 
+[[package]]
+name = "alloy-signer-local"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe8f78cd6b7501c7e813a1eb4a087b72d23af51f5bb66d4e948dc840bdd207d8"
+dependencies = [
+ "alloy-consensus",
+ "alloy-network",
+ "alloy-primitives 0.8.20",
+ "alloy-signer",
+ "async-trait",
+ "k256",
+ "rand 0.8.5",
+ "thiserror 2.0.12",
+]
+
 [[package]]
 name = "alloy-sol-macro"
 version = "0.7.7"
@@ -566,7 +637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d95a94854e420f07e962f7807485856cde359ab99ab6413883e15235ad996e8b"
 dependencies = [
  "alloy-primitives 0.8.20",
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
  "arrayvec",
  "derive_more 1.0.0",
  "nybbles",
@@ -625,6 +696,12 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
+
 [[package]]
 name = "ark-ff"
 version = "0.3.0"
@@ -811,6 +888,16 @@ dependencies = [
  "rustc_version 0.4.1",
 ]
 
+[[package]]
+name = "aurora-engine-modexp"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9"
+dependencies = [
+ "hex",
+ "num",
+]
+
 [[package]]
 name = "auto_impl"
 version = "1.3.0"
@@ -879,6 +966,15 @@ version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
 
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "bit-set"
 version = "0.5.3"
@@ -954,6 +1050,96 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "borsh"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa"
+dependencies = [
+ "borsh-derive 0.9.3",
+ "hashbrown 0.11.2",
+]
+
+[[package]]
+name = "borsh"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee"
+dependencies = [
+ "borsh-derive 0.10.4",
+ "hashbrown 0.13.2",
+]
+
+[[package]]
+name = "borsh-derive"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775"
+dependencies = [
+ "borsh-derive-internal 0.9.3",
+ "borsh-schema-derive-internal 0.9.3",
+ "proc-macro-crate 0.1.5",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-derive"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89"
+dependencies = [
+ "borsh-derive-internal 0.10.4",
+ "borsh-schema-derive-internal 0.10.4",
+ "proc-macro-crate 0.1.5",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-derive-internal"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-derive-internal"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-schema-derive-internal"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-schema-derive-internal"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "branches"
 version = "0.2.1"
@@ -985,6 +1171,26 @@ version = "1.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d"
 
+[[package]]
+name = "bytemuck"
+version = "1.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "byteorder"
 version = "1.5.0"
@@ -1428,6 +1634,17 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "derive_arbitrary"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "derive_more"
 version = "0.99.20"
@@ -1629,6 +1846,17 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "enumn"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.2"
@@ -1782,7 +2010,7 @@ dependencies = [
  "serde",
  "serde_json",
  "syn 2.0.101",
- "toml",
+ "toml 0.8.23",
  "walkdir",
 ]
 
@@ -1973,6 +2201,15 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "fast-math"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2465292146cdfc2011350fe3b1c616ac83cf0faeedb33463ba1c332ed8948d66"
+dependencies = [
+ "ieee754",
+]
+
 [[package]]
 name = "fastrand"
 version = "2.3.0"
@@ -2303,6 +2540,24 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash 0.7.8",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash 0.8.12",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.14.5"
@@ -2647,6 +2902,12 @@ dependencies = [
  "icu_properties",
 ]
 
+[[package]]
+name = "ieee754"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c"
+
 [[package]]
 name = "impl-codec"
 version = "0.6.0"
@@ -2697,6 +2958,7 @@ version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
 dependencies = [
+ "arbitrary",
  "equivalent",
  "hashbrown 0.15.3",
  "serde",
@@ -2883,6 +3145,9 @@ name = "lazy_static"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin 0.9.8",
+]
 
 [[package]]
 name = "libc"
@@ -3021,6 +3286,12 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "mock_instant"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6"
+
 [[package]]
 name = "motsu"
 version = "0.1.0"
@@ -3028,12 +3299,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "744174e5011fed86212d90c1120037da87e0c45fee7a5861c2b3105e41283810"
 dependencies = [
  "const-hex",
- "motsu-proc",
+ "motsu-proc 0.1.1",
  "once_cell",
  "stylus-sdk 0.6.0",
  "tiny-keccak",
 ]
 
+[[package]]
+name = "motsu"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31eab998c3d9d9e2627e291cc44dc98a8f7c30f63bd21181f6468e58ceb70185"
+dependencies = [
+ "alloy-primitives 0.8.20",
+ "alloy-signer-local",
+ "alloy-sol-types 0.8.20",
+ "const-hex",
+ "dashmap",
+ "k256",
+ "motsu-proc 0.9.0",
+ "once_cell",
+ "revm-precompile",
+ "stylus-sdk 0.9.0",
+ "tiny-keccak",
+]
+
 [[package]]
 name = "motsu-proc"
 version = "0.1.1"
@@ -3045,6 +3335,17 @@ dependencies = [
  "syn 2.0.101",
 ]
 
+[[package]]
+name = "motsu-proc"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a218b460f9f4f7a22fbfc5ca7d4917c0790a94be431bbc6cdfc2e75263ff2e28"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "native-tls"
 version = "0.2.14"
@@ -3068,6 +3369,20 @@ version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
 
+[[package]]
+name = "num"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
 [[package]]
 name = "num-bigint"
 version = "0.4.6"
@@ -3078,6 +3393,15 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "num-conv"
 version = "0.1.0"
@@ -3093,6 +3417,28 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.2.19"
@@ -3128,7 +3474,7 @@ version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
 dependencies = [
- "proc-macro-crate",
+ "proc-macro-crate 3.3.0",
  "proc-macro2",
  "quote",
  "syn 2.0.101",
@@ -3263,7 +3609,7 @@ version = "3.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a"
 dependencies = [
- "proc-macro-crate",
+ "proc-macro-crate 3.3.0",
  "proc-macro2",
  "quote",
  "syn 2.0.101",
@@ -3527,6 +3873,15 @@ dependencies = [
  "uint",
 ]
 
+[[package]]
+name = "proc-macro-crate"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
+dependencies = [
+ "toml 0.5.11",
+]
+
 [[package]]
 name = "proc-macro-crate"
 version = "3.3.0"
@@ -3611,6 +3966,69 @@ dependencies = [
  "unarray",
 ]
 
+[[package]]
+name = "proptest-derive"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
+[[package]]
+name = "pyth-receiver-stylus"
+version = "0.1.11"
+dependencies = [
+ "alloy-primitives 0.8.20",
+ "alloy-sol-types 0.8.20",
+ "byteorder",
+ "dotenv",
+ "ethers",
+ "eyre",
+ "hex",
+ "mock_instant",
+ "motsu 0.9.1",
+ "pythnet-sdk",
+ "serde",
+ "stylus-sdk 0.9.0",
+ "tokio",
+ "wormhole-contract",
+ "wormhole-vaas",
+]
+
+[[package]]
+name = "pyth-sdk"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5c805ba3dfb5b7ed6a8ffa62ec38391f485a79c7cf6b3b11d3bd44fb0325824"
+dependencies = [
+ "borsh 0.9.3",
+ "borsh-derive 0.9.3",
+ "hex",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "pythnet-sdk"
+version = "2.3.1"
+dependencies = [
+ "bincode",
+ "borsh 0.10.4",
+ "bytemuck",
+ "byteorder",
+ "fast-math",
+ "hex",
+ "pyth-sdk",
+ "rustc_version 0.4.1",
+ "serde",
+ "sha3",
+ "slow_primes",
+ "thiserror 1.0.69",
+]
+
 [[package]]
 name = "quick-error"
 version = "1.2.3"
@@ -3862,6 +4280,45 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "revm-precompile"
+version = "16.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99743c3a2cac341084cc15ac74286c4bf34a0941ebf60aa420cfdb9f81f72f9f"
+dependencies = [
+ "aurora-engine-modexp",
+ "blst",
+ "c-kzg",
+ "cfg-if 1.0.0",
+ "k256",
+ "once_cell",
+ "revm-primitives",
+ "ripemd",
+ "secp256k1",
+ "sha2",
+ "substrate-bn",
+]
+
+[[package]]
+name = "revm-primitives"
+version = "15.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f987564210317706def498421dfba2ae1af64a8edce82c6102758b48133fcb"
+dependencies = [
+ "alloy-eip2930",
+ "alloy-eip7702",
+ "alloy-primitives 0.8.20",
+ "auto_impl",
+ "bitflags 2.9.1",
+ "bitvec",
+ "c-kzg",
+ "cfg-if 1.0.0",
+ "dyn-clone",
+ "enumn",
+ "hex",
+ "serde",
+]
+
 [[package]]
 name = "rfc6979"
 version = "0.4.0"
@@ -3881,7 +4338,7 @@ dependencies = [
  "cc",
  "libc",
  "once_cell",
- "spin",
+ "spin 0.5.2",
  "untrusted 0.7.1",
  "web-sys",
  "winapi",
@@ -3938,7 +4395,8 @@ version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4"
 dependencies = [
- "alloy-rlp",
+ "alloy-rlp 0.3.12",
+ "arbitrary",
  "ark-ff 0.3.0",
  "ark-ff 0.4.2",
  "bytes",
@@ -3965,6 +4423,24 @@ version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18"
 
+[[package]]
+name = "ruint2"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b066b8e4fcea7fae86b6932d2449670b6b5545b7e8407841b2d3a916ff2a9f86"
+dependencies = [
+ "derive_more 0.99.20",
+ "ruint2-macro",
+ "rustc_version 0.4.1",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ruint2-macro"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89dc553bc0cf4512a8b96caa2e21ed5f6e4b66bf28a1bd08fd9eb07c0b39b28c"
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.25"
@@ -3976,6 +4452,9 @@ name = "rustc-hash"
 version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+dependencies = [
+ "rand 0.8.5",
+]
 
 [[package]]
 name = "rustc-hex"
@@ -4123,7 +4602,7 @@ version = "2.11.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf"
 dependencies = [
- "proc-macro-crate",
+ "proc-macro-crate 3.3.0",
  "proc-macro2",
  "quote",
  "syn 2.0.101",
@@ -4138,6 +4617,30 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "schemars"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -4180,6 +4683,25 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "secp256k1"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
+dependencies = [
+ "rand 0.8.5",
+ "secp256k1-sys",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "security-framework"
 version = "2.11.1"
@@ -4277,6 +4799,17 @@ dependencies = [
  "syn 2.0.101",
 ]
 
+[[package]]
+name = "serde_derive_internals"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.101",
+]
+
 [[package]]
 name = "serde_json"
 version = "1.0.140"
@@ -4401,6 +4934,15 @@ version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
 
+[[package]]
+name = "slow_primes"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58267dd2fbaa6dceecba9e3e106d2d90a2b02497c0e8b01b8759beccf5113938"
+dependencies = [
+ "num",
+]
+
 [[package]]
 name = "smallvec"
 version = "1.15.1"
@@ -4440,6 +4982,12 @@ 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"
+
 [[package]]
 name = "spki"
 version = "0.7.3"
@@ -4536,20 +5084,6 @@ dependencies = [
  "dyn-clone",
 ]
 
-[[package]]
-name = "stylus-hello-world"
-version = "0.1.11"
-dependencies = [
- "alloy-primitives 0.8.20",
- "alloy-sol-types 0.8.20",
- "dotenv",
- "ethers",
- "eyre",
- "hex",
- "stylus-sdk 0.9.0",
- "tokio",
-]
-
 [[package]]
 name = "stylus-proc"
 version = "0.6.1"
@@ -4603,7 +5137,6 @@ dependencies = [
  "hex",
  "keccak-const",
  "lazy_static",
- "regex",
  "stylus-proc 0.6.1",
 ]
 
@@ -4643,6 +5176,19 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "substrate-bn"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c"
+dependencies = [
+ "byteorder",
+ "crunchy",
+ "lazy_static",
+ "rand 0.8.5",
+ "rustc-hex",
+]
+
 [[package]]
 name = "subtle"
 version = "2.6.1"
@@ -5010,6 +5556,15 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "toml"
 version = "0.8.23"
@@ -5155,7 +5710,7 @@ dependencies = [
  "serde_json",
  "target-triple",
  "termcolor",
- "toml",
+ "toml 0.8.23",
 ]
 
 [[package]]
@@ -5666,8 +6221,18 @@ dependencies = [
  "base64 0.21.7",
  "k256",
  "mini-alloc 0.4.2",
- "motsu",
- "stylus-sdk 0.6.0",
+ "motsu 0.1.0",
+ "stylus-sdk 0.9.0",
+]
+
+[[package]]
+name = "wormhole-vaas"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cd4775f602f96f911d079c81a12fc964fab8632d56229bbb953335bae043d73"
+dependencies = [
+ "alloy-primitives 0.2.0",
+ "hex-literal",
 ]
 
 [[package]]

+ 2 - 3
target_chains/stylus/Cargo.toml

@@ -1,8 +1,7 @@
 [workspace]
 members = [
     "contracts/wormhole",
-    "contracts/pyth-receiver"
-]
+    "contracts/pyth-receiver"]
 resolver = "2"
 
 [workspace.package]
@@ -12,7 +11,7 @@ repository = "https://github.com/pyth-network/pyth-crosschain"
 version = "0.1.0"
 
 [workspace.dependencies]
-stylus-sdk = { version = "0.6.0", default-features = false }
+stylus-sdk = { version = "0.9.0", default-features = false }
 alloy-primitives = { version = "0.7.6", default-features = false }
 mini-alloc = { version = "0.4.2", default-features = false }
 motsu = "0.1.0"

+ 16 - 12
target_chains/stylus/contracts/pyth-receiver/Cargo.toml

@@ -1,18 +1,21 @@
 [package]
-name = "stylus-hello-world"
+name = "pyth-receiver-stylus"
 version = "0.1.11"
 edition = "2021"
-license = "MIT OR Apache-2.0"
-homepage = "https://github.com/OffchainLabs/stylus-hello-world"
-repository = "https://github.com/OffchainLabs/stylus-hello-world"
-keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
-description = "Stylus hello world example"
+homepage = "https://github.com/pyth-network/pyth-crosschain"
+repository = "https://github.com/pyth-network/pyth-crosschain"
+keywords = ["arbitrum", "ethereum", "stylus", "alloy", "pyth"]
+description = "Pyth receiver contract for the Arbitrum networks, built on Arbitrum Stylus"
 
 [dependencies]
 alloy-primitives = "=0.8.20"
 alloy-sol-types = "=0.8.20"
 stylus-sdk = "0.9.0"
+byteorder = { version = "1.4.3" }
 hex = { version = "0.4", default-features = false }
+pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk" }
+serde = { version = "1.0", features = ["derive"] }
+wormhole-vaas = "0.1.1"
 
 [dev-dependencies]
 alloy-primitives = { version = "=0.8.20", features = ["sha3-keccak"] }
@@ -21,6 +24,9 @@ ethers = "2.0"
 eyre = "0.6.8"
 stylus-sdk = { version = "0.9.0", features = ["stylus-test"] }
 dotenv = "0.15.0"
+motsu = "0.9.0"
+wormhole-contract = { path = "../wormhole" }
+mock_instant = "0.6.0"
 
 [features]
 default = ["mini-alloc"]
@@ -29,7 +35,7 @@ debug = ["stylus-sdk/debug"]
 mini-alloc = ["stylus-sdk/mini-alloc"]
 
 [[bin]]
-name = "stylus-hello-world"
+name = "pyth-receiver-stylus"
 path = "src/main.rs"
 
 [lib]
@@ -37,10 +43,8 @@ crate-type = ["lib", "cdylib"]
 
 [profile.release]
 codegen-units = 1
-strip = true
+opt-level = "s"
 lto = true
+debug = false
 panic = "abort"
-
-# If you need to reduce the binary size, it is advisable to try other
-# optimization levels, such as "s" and "z"
-opt-level = 3
+overflow-checks = true

+ 31 - 2
target_chains/stylus/contracts/pyth-receiver/src/error.rs

@@ -1,7 +1,22 @@
 use alloc::vec::Vec;
 
+#[derive(PartialEq)]
 pub enum PythReceiverError {
-    PriceUnavailable
+    PriceUnavailable,
+    InvalidUpdateData,
+    VaaVerificationFailed,
+    InvalidVaa,
+    InvalidWormholeMessage,
+    InvalidMerkleProof,
+    InvalidAccumulatorMessage,
+    InvalidMerkleRoot,
+    InvalidMerklePath,
+    InvalidUnknownSource,
+    NewPriceUnavailable,
+    InvalidAccumulatorMessageType,
+    InsufficientFee,
+    InvalidEmitterAddress,
+    TooManyUpdates,
 }
 
 impl core::fmt::Debug for PythReceiverError {
@@ -14,6 +29,20 @@ impl From<PythReceiverError> for Vec<u8> {
     fn from(error: PythReceiverError) -> Vec<u8> {
         vec![match error {
             PythReceiverError::PriceUnavailable => 1,
+            PythReceiverError::InvalidUpdateData => 2,
+            PythReceiverError::VaaVerificationFailed => 3,
+            PythReceiverError::InvalidVaa => 4,
+            PythReceiverError::InvalidWormholeMessage => 5,
+            PythReceiverError::InvalidMerkleProof => 6,
+            PythReceiverError::InvalidAccumulatorMessage => 7,
+            PythReceiverError::InvalidMerkleRoot => 8,
+            PythReceiverError::InvalidMerklePath => 9,
+            PythReceiverError::InvalidUnknownSource => 10,
+            PythReceiverError::NewPriceUnavailable => 11,
+            PythReceiverError::InvalidAccumulatorMessageType => 12,
+            PythReceiverError::InsufficientFee => 13,
+            PythReceiverError::InvalidEmitterAddress => 14,
+            PythReceiverError::TooManyUpdates => 15,
         }]
     }
-}
+}

+ 345 - 0
target_chains/stylus/contracts/pyth-receiver/src/integration_tests.rs

@@ -0,0 +1,345 @@
+#[cfg(test)]
+mod test {
+    use crate::error::PythReceiverError;
+    use crate::test_data::*;
+    use crate::PythReceiver;
+    use alloy_primitives::{Address, U256};
+    use mock_instant::global::MockClock;
+    use motsu::prelude::*;
+    use pythnet_sdk::wire::v1::{AccumulatorUpdateData, Proof};
+    use std::time::Duration;
+    use wormhole_contract::WormholeContract;
+    const TEST_PRICE_ID: [u8; 32] = [
+        0xe6, 0x2d, 0xf6, 0xc8, 0xb4, 0xa8, 0x5f, 0xe1, 0xa6, 0x7d, 0xb4, 0x4d, 0xc1, 0x2d, 0xe5,
+        0xdb, 0x33, 0x0f, 0x7a, 0xc6, 0x6b, 0x72, 0xdc, 0x65, 0x8a, 0xfe, 0xdf, 0x0f, 0x4a, 0x41,
+        0x5b, 0x43,
+    ];
+
+    const PYTHNET_CHAIN_ID: u16 = 26;
+    const PYTHNET_EMITTER_ADDRESS: [u8; 32] = [
+        0xe1, 0x01, 0xfa, 0xed, 0xac, 0x58, 0x51, 0xe3, 0x2b, 0x9b, 0x23, 0xb5, 0xf9, 0x41, 0x1a,
+        0x8c, 0x2b, 0xac, 0x4a, 0xae, 0x3e, 0xd4, 0xdd, 0x7b, 0x81, 0x1d, 0xd1, 0xa7, 0x2e, 0xa4,
+        0xaa, 0x71,
+    ];
+
+    const CHAIN_ID: u16 = 60051;
+    const GOVERNANCE_CHAIN_ID: u16 = 1;
+    const GOVERNANCE_CONTRACT: U256 = U256::from_limbs([4, 0, 0, 0]);
+
+    const SINGLE_UPDATE_FEE_IN_WEI: U256 = U256::from_limbs([100, 0, 0, 0]);
+    const TRANSACTION_FEE_IN_WEI: U256 = U256::from_limbs([32, 0, 0, 0]);
+
+    #[cfg(test)]
+    fn mock_get_update_fee(update_data: Vec<Vec<u8>>) -> Result<U256, PythReceiverError> {
+        let mut total_num_updates: u64 = 0;
+        for data in &update_data {
+            let update_data_array: &[u8] = &data;
+            let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
+                .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
+            match accumulator_update.proof {
+                Proof::WormholeMerkle { vaa: _, updates } => {
+                    let num_updates = u64::try_from(updates.len())
+                        .map_err(|_| PythReceiverError::TooManyUpdates)?;
+                    total_num_updates += num_updates;
+                }
+            }
+        }
+        Ok(get_total_fee(total_num_updates))
+    }
+
+    fn get_total_fee(total_num_updates: u64) -> U256 {
+        U256::from(total_num_updates).saturating_mul(SINGLE_UPDATE_FEE_IN_WEI)
+            + TRANSACTION_FEE_IN_WEI
+    }
+
+    #[cfg(test)]
+    fn pyth_wormhole_init(
+        pyth_contract: &Contract<PythReceiver>,
+        wormhole_contract: &Contract<WormholeContract>,
+        alice: &Address,
+    ) {
+        let guardians = current_guardians();
+        let governance_contract =
+            Address::from_slice(&GOVERNANCE_CONTRACT.to_be_bytes::<32>()[12..32]);
+        wormhole_contract
+            .sender(*alice)
+            .initialize(
+                guardians,
+                4,
+                CHAIN_ID,
+                GOVERNANCE_CHAIN_ID,
+                governance_contract,
+            )
+            .unwrap();
+
+        let single_update_fee = SINGLE_UPDATE_FEE_IN_WEI;
+        let valid_time_period = U256::from(3600u64);
+
+        let data_source_chain_ids = vec![PYTHNET_CHAIN_ID];
+        let data_source_emitter_addresses = vec![PYTHNET_EMITTER_ADDRESS];
+
+        let governance_chain_id = 1u16;
+        let governance_emitter_address = [3u8; 32];
+        let governance_initial_sequence = 0u64;
+
+        pyth_contract.sender(*alice).initialize(
+            wormhole_contract.address(),
+            single_update_fee,
+            valid_time_period,
+            data_source_chain_ids,
+            data_source_emitter_addresses,
+            governance_chain_id,
+            governance_emitter_address,
+            governance_initial_sequence,
+        );
+    }
+
+    #[motsu::test]
+    fn tests_pyth_end_to_end_with_update(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let update_data = good_update1();
+        let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
+
+        alice.fund(update_fee);
+
+        let result = pyth_contract
+            .sender_and_value(alice, update_fee)
+            .update_price_feeds(update_data);
+        assert!(result.is_ok());
+
+        let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
+        assert!(price_result.is_ok());
+        assert_eq!(price_result.unwrap(), good_update1_results());
+    }
+
+    #[motsu::test]
+    fn test_update_price_feed_reverts_insufficient_fee(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        alice.fund(U256::from(200));
+
+        let update_data = good_update1();
+        let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
+        let small_update_fee = update_fee / U256::from(2);
+
+        let result = pyth_contract
+            .sender_and_value(alice, small_update_fee)
+            .update_price_feeds(update_data);
+        assert!(result.is_err());
+        assert_eq!(result.unwrap_err(), PythReceiverError::InsufficientFee);
+    }
+
+    #[motsu::test]
+    fn test_get_price_after_multiple_different_updates_returns_recent_price(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let update_data1 = good_update1();
+        let update_fee1 = mock_get_update_fee(update_data1.clone()).unwrap();
+
+        let update_data2 = good_update2();
+        let update_fee2 = mock_get_update_fee(update_data2.clone()).unwrap();
+
+        alice.fund(update_fee1 + update_fee2);
+
+        let result1 = pyth_contract
+            .sender_and_value(alice, update_fee1)
+            .update_price_feeds(update_data1);
+        assert!(result1.is_ok());
+
+        let result2 = pyth_contract
+            .sender_and_value(alice, update_fee2)
+            .update_price_feeds(update_data2);
+        assert!(result2.is_ok());
+
+        let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
+        assert!(price_result.is_ok());
+        assert_eq!(price_result.unwrap(), good_update2_results());
+    }
+
+    #[motsu::test]
+    fn test_get_price_with_no_update_reverts_with_price_unavailable(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let price_result = pyth_contract.sender(alice).get_price_unsafe(TEST_PRICE_ID);
+        assert!(price_result.is_err());
+        assert_eq!(
+            price_result.unwrap_err(),
+            PythReceiverError::PriceUnavailable
+        );
+    }
+
+    #[motsu::test]
+    fn test_get_price_no_older_than_with_random_id_reverts_with_price_unavailable(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        MockClock::set_time(Duration::from_secs(1761573860)); // less than good_update2().timestamp + 1s
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let random_id: [u8; 32] = [
+            0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
+            0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78,
+            0x9a, 0xbc, 0xde, 0xf0,
+        ];
+
+        let price_result = pyth_contract
+            .sender(alice)
+            .get_price_no_older_than(random_id, 3600);
+        assert!(price_result.is_err());
+        assert_eq!(
+            price_result.unwrap_err(),
+            PythReceiverError::PriceUnavailable
+        );
+    }
+
+    #[motsu::test]
+    fn test_get_price_no_older_than_where_update_younger_than_max_age_returns_price(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        MockClock::set_time(Duration::from_secs(1761573860));
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let update_data = good_update2();
+        let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
+
+        alice.fund(update_fee);
+
+        let result = pyth_contract
+            .sender_and_value(alice, update_fee)
+            .update_price_feeds(update_data);
+        assert!(result.is_ok());
+
+        let price_result = pyth_contract
+            .sender(alice)
+            .get_price_no_older_than(TEST_PRICE_ID, u64::MAX);
+        assert!(price_result.is_ok());
+        assert_eq!(price_result.unwrap(), good_update2_results());
+    }
+
+    #[motsu::test]
+    fn test_get_price_no_older_than_reverts_too_old(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        MockClock::set_time(Duration::from_secs(1761573860));
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let update_data = good_update2();
+        let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
+
+        alice.fund(update_fee);
+
+        let result = pyth_contract
+            .sender_and_value(alice, update_fee)
+            .update_price_feeds(update_data);
+        assert!(result.is_ok());
+
+        let price_result = pyth_contract
+            .sender(alice)
+            .get_price_no_older_than(TEST_PRICE_ID, 1);
+        assert!(price_result.is_err());
+        assert_eq!(
+            price_result.unwrap_err(),
+            PythReceiverError::NewPriceUnavailable
+        );
+    }
+
+    #[motsu::test]
+    fn test_multiple_updates_in_same_vaa_different_ids_updates_both(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let update_data = multiple_updates_same_vaa();
+        let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
+
+        alice.fund(update_fee);
+
+        let result = pyth_contract
+            .sender_and_value(alice, update_fee)
+            .update_price_feeds(update_data);
+        assert!(result.is_ok());
+
+        let first_id: [u8; 32] = [
+            0xe6, 0x2d, 0xf6, 0xc8, 0xb4, 0xa8, 0x5f, 0xe1, 0xa6, 0x7d, 0xb4, 0x4d, 0xc1, 0x2d,
+            0xe5, 0xdb, 0x33, 0x0f, 0x7a, 0xc6, 0x6b, 0x72, 0xdc, 0x65, 0x8a, 0xfe, 0xdf, 0x0f,
+            0x4a, 0x41, 0x5b, 0x43,
+        ];
+        let second_id: [u8; 32] = [
+            0xff, 0x61, 0x49, 0x1a, 0x93, 0x11, 0x12, 0xdd, 0xf1, 0xbd, 0x81, 0x47, 0xcd, 0x1b,
+            0x64, 0x13, 0x75, 0xf7, 0x9f, 0x58, 0x25, 0x12, 0x6d, 0x66, 0x54, 0x80, 0x87, 0x46,
+            0x34, 0xfd, 0x0a, 0xce,
+        ];
+
+        let first_price_result = pyth_contract.sender(alice).get_price_unsafe(first_id);
+        assert!(first_price_result.is_ok());
+        assert_eq!(first_price_result.unwrap(), multiple_updates_results()[0]);
+
+        let second_price_result = pyth_contract.sender(alice).get_price_unsafe(second_id);
+        assert!(second_price_result.is_ok());
+        assert_eq!(second_price_result.unwrap(), multiple_updates_results()[1]);
+    }
+
+    #[motsu::test]
+    fn test_multiple_updates_different_ids_updates_both(
+        pyth_contract: Contract<PythReceiver>,
+        wormhole_contract: Contract<WormholeContract>,
+        alice: Address,
+    ) {
+        pyth_wormhole_init(&pyth_contract, &wormhole_contract, &alice);
+
+        let update_data = multiple_updates_diff_vaa();
+        let update_fee = mock_get_update_fee(update_data.clone()).unwrap();
+
+        alice.fund(update_fee);
+
+        let result = pyth_contract
+            .sender_and_value(alice, update_fee)
+            .update_price_feeds(update_data);
+        assert!(result.is_ok());
+
+        let first_id: [u8; 32] = [
+            0x3f, 0xa4, 0x25, 0x28, 0x48, 0xf9, 0xf0, 0xa1, 0x48, 0x0b, 0xe6, 0x27, 0x45, 0xa4,
+            0x62, 0x9d, 0x9e, 0xb1, 0x32, 0x2a, 0xeb, 0xab, 0x8a, 0x79, 0x1e, 0x34, 0x4b, 0x3b,
+            0x9c, 0x1a, 0xdc, 0xf5,
+        ];
+        let second_id: [u8; 32] = TEST_PRICE_ID;
+
+        let first_price_result = pyth_contract.sender(alice).get_price_unsafe(first_id);
+        assert!(first_price_result.is_ok());
+        assert_eq!(
+            first_price_result.unwrap(),
+            multiple_updates_diff_vaa_results()[0]
+        );
+
+        let second_price_result = pyth_contract.sender(alice).get_price_unsafe(second_id);
+        assert!(second_price_result.is_ok());
+        assert_eq!(
+            second_price_result.unwrap(),
+            multiple_updates_diff_vaa_results()[1]
+        );
+    }
+}

+ 287 - 28
target_chains/stylus/contracts/pyth-receiver/src/lib.rs

@@ -5,26 +5,62 @@
 #[macro_use]
 extern crate alloc;
 
-mod structs;
 mod error;
+#[cfg(test)]
+mod integration_tests;
+mod structs;
+#[cfg(test)]
+mod test_data;
+
+#[cfg(test)]
+use mock_instant::global::MockClock;
 
 use alloc::vec::Vec;
-use stylus_sdk::{alloy_primitives::{U256, U64, I32, I64, FixedBytes},
-                prelude::*, 
-                storage::{StorageAddress, StorageVec, StorageMap, StorageUint, StorageBool, StorageU256}};
+use stylus_sdk::{
+    alloy_primitives::{Address, FixedBytes, I32, I64, U16, U256, U32, U64},
+    call::Call,
+    prelude::*,
+    storage::{
+        StorageAddress, StorageBool, StorageFixedBytes, StorageMap, StorageU16, StorageU256,
+        StorageUint, StorageVec,
+    },
+};
 
-use structs::{DataSourceStorage, PriceInfoReturn, PriceInfoStorage};
-use error::{PythReceiverError};
+use error::PythReceiverError;
+use pythnet_sdk::{
+    accumulators::merkle::{MerklePath, MerkleRoot},
+    hashers::keccak256_160::Keccak160,
+    messages::Message,
+    wire::{
+        from_slice,
+        v1::{
+            AccumulatorUpdateData, Proof, WormholeMessage, WormholePayload,
+            PYTHNET_ACCUMULATOR_UPDATE_MAGIC,
+        },
+    },
+};
+use structs::{DataSource, DataSourceStorage, PriceInfoReturn, PriceInfoStorage};
+use wormhole_vaas::{Readable, Vaa, Writeable};
+
+sol_interface! {
+    interface IWormholeContract {
+        function initialize(address[] memory initial_guardians, uint16 chain_id, uint16 governance_chain_id, address governance_contract) external;
+        function getGuardianSet(uint32 index) external view returns (uint8[] memory);
+        function parseAndVerifyVm(uint8[] memory encoded_vaa) external view returns (uint8[] memory);
+        function quorum(uint32 num_guardians) external pure returns (uint32);
+    }
+}
 
 #[storage]
 #[entrypoint]
 pub struct PythReceiver {
     pub wormhole: StorageAddress,
     pub valid_data_sources: StorageVec<DataSourceStorage>,
-    pub is_valid_data_source: StorageMap<FixedBytes<32>, StorageBool>,
+    pub is_valid_data_source: StorageMap<DataSource, StorageBool>,
     pub single_update_fee_in_wei: StorageU256,
     pub valid_time_period_seconds: StorageU256,
-    pub governance_data_source: DataSourceStorage,
+    pub governance_data_source_chain_id: StorageU16,
+    pub governance_data_source_emitter_address: StorageFixedBytes<32>,
     pub last_executed_governance_sequence: StorageUint<64, 1>,
     pub governance_data_source_index: StorageUint<32, 1>,
     pub latest_price_info: StorageMap<FixedBytes<32>, PriceInfoStorage>,
@@ -33,11 +69,53 @@ pub struct PythReceiver {
 
 #[public]
 impl PythReceiver {
-    pub fn get_price_unsafe(&self, _id: [u8; 32]) -> Result<PriceInfoReturn, PythReceiverError> {
-        let id_fb = FixedBytes::<32>::from(_id);
-        
+    pub fn initialize(
+        &mut self,
+        wormhole: Address,
+        single_update_fee_in_wei: U256,
+        valid_time_period_seconds: U256,
+        data_source_emitter_chain_ids: Vec<u16>,
+        data_source_emitter_addresses: Vec<[u8; 32]>,
+        governance_emitter_chain_id: u16,
+        governance_emitter_address: [u8; 32],
+        governance_initial_sequence: u64,
+    ) {
+        self.wormhole.set(wormhole);
+        self.single_update_fee_in_wei.set(single_update_fee_in_wei);
+        self.valid_time_period_seconds
+            .set(valid_time_period_seconds);
+
+        self.governance_data_source_chain_id
+            .set(U16::from(governance_emitter_chain_id));
+        self.governance_data_source_emitter_address
+            .set(FixedBytes::<32>::from(governance_emitter_address));
+
+        self.last_executed_governance_sequence
+            .set(U64::from(governance_initial_sequence));
+        self.governance_data_source_index.set(U32::ZERO);
+
+        for (i, chain_id) in data_source_emitter_chain_ids.iter().enumerate() {
+            let emitter_address = FixedBytes::<32>::from(data_source_emitter_addresses[i]);
+
+            // Create a new data source storage slot
+            let mut data_source = self.valid_data_sources.grow();
+            data_source.chain_id.set(U16::from(*chain_id));
+            data_source.emitter_address.set(emitter_address);
+
+            let data_source_key = DataSource {
+                chain_id: U16::from(*chain_id),
+                emitter_address: emitter_address,
+            };
+
+            self.is_valid_data_source.setter(data_source_key).set(true);
+        }
+    }
+
+    pub fn get_price_unsafe(&self, id: [u8; 32]) -> Result<PriceInfoReturn, PythReceiverError> {
+        let id_fb = FixedBytes::<32>::from(id);
+
         let price_info = self.latest_price_info.get(id_fb);
-        
+
         if price_info.publish_time.get() == U64::ZERO {
             return Err(PythReceiverError::PriceUnavailable);
         }
@@ -52,24 +130,65 @@ impl PythReceiver {
         ))
     }
 
-    pub fn get_price_no_older_than(&self, _id: [u8; 32], _age: u64) -> Result<PriceInfoReturn, PythReceiverError> {
-        let price_info = self.get_price_unsafe(_id)?;
-        if !self.is_no_older_than(price_info.0, _age) {
-            return Err(PythReceiverError::PriceUnavailable);
+    pub fn get_price_no_older_than(
+        &self,
+        id: [u8; 32],
+        age: u64,
+    ) -> Result<PriceInfoReturn, PythReceiverError> {
+        let price_info = self.get_price_unsafe(id)?;
+        if !self.is_no_older_than(price_info.0, age) {
+            return Err(PythReceiverError::NewPriceUnavailable);
         }
         Ok(price_info)
     }
 
-    pub fn get_ema_price_unsafe(&self, _id: [u8; 32]) -> PriceInfoReturn {
-        (U64::ZERO, I32::ZERO, I64::ZERO, U64::ZERO, I64::ZERO, U64::ZERO)
+    pub fn get_ema_price_unsafe(&self, id: [u8; 32]) -> Result<PriceInfoReturn, PythReceiverError> {
+        let id_fb = FixedBytes::<32>::from(id);
+        let price_info = self.latest_price_info.get(id_fb);
+
+        if price_info.publish_time.get() == U64::ZERO {
+            return Err(PythReceiverError::PriceUnavailable);
+        }
+
+        Ok((
+            price_info.publish_time.get(),
+            price_info.expo.get(),
+            price_info.ema_price.get(),
+            price_info.ema_conf.get(),
+            price_info.ema_price.get(),
+            price_info.ema_conf.get(),
+        ))
     }
 
-    pub fn get_ema_price_no_older_than(&self, _id: [u8; 32], _age: u64) -> PriceInfoReturn {
-        (U64::ZERO, I32::ZERO, I64::ZERO, U64::ZERO, I64::ZERO, U64::ZERO)
+    pub fn get_ema_price_no_older_than(
+        &self,
+        id: [u8; 32],
+        age: u64,
+    ) -> Result<PriceInfoReturn, PythReceiverError> {
+        let price_info = self.get_ema_price_unsafe(id)?;
+        if !self.is_no_older_than(price_info.0, age) {
+            return Err(PythReceiverError::NewPriceUnavailable);
+        }
+        Ok(price_info)
     }
 
-    pub fn update_price_feeds(&mut self, _update_data: Vec<Vec<u8>>) {
-        // dummy implementation
+    #[payable]
+    pub fn update_price_feeds(
+        &mut self,
+        update_data: Vec<Vec<u8>>,
+    ) -> Result<(), PythReceiverError> {
+        for data in &update_data {
+            self.update_price_feeds_internal(data.clone())?;
+        }
+
+        let total_fee = self.get_update_fee(update_data)?;
+
+        let value = self.vm().msg_value();
+
+        if value < total_fee {
+            return Err(PythReceiverError::InsufficientFee);
+        }
+        Ok(())
     }
 
     pub fn update_price_feeds_if_necessary(
@@ -81,8 +200,127 @@ impl PythReceiver {
         // dummy implementation
     }
 
-    pub fn get_update_fee(&self, _update_data: Vec<Vec<u8>>) -> U256 {
-        U256::from(0u8)
+    fn update_price_feeds_internal(
+        &mut self,
+        update_data: Vec<u8>,
+    ) -> Result<(), PythReceiverError> {
+        let update_data_array: &[u8] = &update_data;
+        // Check the first 4 bytes of the update_data_array for the magic header
+        if update_data_array.len() < 4 {
+            return Err(PythReceiverError::InvalidUpdateData);
+        }
+
+        let mut header = [0u8; 4];
+        header.copy_from_slice(&update_data_array[0..4]);
+
+        if &header != PYTHNET_ACCUMULATOR_UPDATE_MAGIC {
+            return Err(PythReceiverError::InvalidAccumulatorMessage);
+        }
+
+        let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
+            .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
+
+        match accumulator_update.proof {
+            Proof::WormholeMerkle { vaa, updates } => {
+                let wormhole: IWormholeContract = IWormholeContract::new(self.wormhole.get());
+                let config = Call::new();
+                wormhole
+                    .parse_and_verify_vm(config, Vec::from(vaa.clone()))
+                    .map_err(|_| PythReceiverError::InvalidWormholeMessage)?;
+
+                let vaa = Vaa::read(&mut Vec::from(vaa.clone()).as_slice())
+                    .map_err(|_| PythReceiverError::VaaVerificationFailed)?;
+
+                let cur_emitter_address: &[u8; 32] = vaa
+                    .body
+                    .emitter_address
+                    .as_slice()
+                    .try_into()
+                    .map_err(|_| PythReceiverError::InvalidEmitterAddress)?;
+
+                let cur_data_source = DataSource {
+                    chain_id: U16::from(vaa.body.emitter_chain),
+                    emitter_address: FixedBytes::from(cur_emitter_address),
+                };
+
+                if !self.is_valid_data_source.get(cur_data_source) {
+                    return Err(PythReceiverError::InvalidWormholeMessage);
+                }
+
+                let root_digest: MerkleRoot<Keccak160> = parse_wormhole_proof(vaa)?;
+
+                for update in updates {
+                    let message_vec = Vec::from(update.message);
+                    let proof: MerklePath<Keccak160> = update.proof;
+
+                    if !root_digest.check(proof, &message_vec) {
+                        return Err(PythReceiverError::InvalidMerkleProof);
+                    }
+
+                    let msg = from_slice::<byteorder::BE, Message>(&message_vec)
+                        .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
+
+                    match msg {
+                        Message::PriceFeedMessage(price_feed_message) => {
+                            let price_id_fb: FixedBytes<32> =
+                                FixedBytes::from(price_feed_message.feed_id);
+                            let mut recent_price_info = self.latest_price_info.setter(price_id_fb);
+
+                            if recent_price_info.publish_time.get()
+                                < U64::from(price_feed_message.publish_time)
+                                || recent_price_info.price.get() == I64::ZERO
+                            {
+                                recent_price_info
+                                    .publish_time
+                                    .set(U64::from(price_feed_message.publish_time));
+                                recent_price_info.price.set(I64::from_le_bytes(
+                                    price_feed_message.price.to_le_bytes(),
+                                ));
+                                recent_price_info
+                                    .conf
+                                    .set(U64::from(price_feed_message.conf));
+                                recent_price_info.expo.set(I32::from_le_bytes(
+                                    price_feed_message.exponent.to_le_bytes(),
+                                ));
+                                recent_price_info.ema_price.set(I64::from_le_bytes(
+                                    price_feed_message.ema_price.to_le_bytes(),
+                                ));
+                                recent_price_info
+                                    .ema_conf
+                                    .set(U64::from(price_feed_message.ema_conf));
+                            }
+                        }
+                        _ => {
+                            return Err(PythReceiverError::InvalidAccumulatorMessageType);
+                        }
+                    }
+                }
+            }
+        };
+
+        Ok(())
+    }
+
+    fn get_update_fee(&self, update_data: Vec<Vec<u8>>) -> Result<U256, PythReceiverError> {
+        let mut total_num_updates: u64 = 0;
+        for data in &update_data {
+            let update_data_array: &[u8] = &data;
+            let accumulator_update = AccumulatorUpdateData::try_from_slice(&update_data_array)
+                .map_err(|_| PythReceiverError::InvalidAccumulatorMessage)?;
+            match accumulator_update.proof {
+                Proof::WormholeMerkle { vaa: _, updates } => {
+                    let num_updates = u64::try_from(updates.len())
+                        .map_err(|_| PythReceiverError::TooManyUpdates)?;
+                    total_num_updates += num_updates;
+                }
+            }
+        }
+        Ok(self.get_total_fee(total_num_updates))
+    }
+
+    fn get_total_fee(&self, total_num_updates: u64) -> U256 {
+        U256::from(total_num_updates).saturating_mul(self.single_update_fee_in_wei.get())
+            + self.transaction_fee_in_wei.get()
     }
 
     pub fn get_twap_update_fee(&self, _update_data: Vec<Vec<u8>>) -> U256 {
@@ -131,9 +369,30 @@ impl PythReceiver {
     }
 
     fn is_no_older_than(&self, publish_time: U64, max_age: u64) -> bool {
-        let current_u64: u64 = self.vm().block_timestamp();
-        let publish_time_u64: u64 = publish_time.to::<u64>();
-        
-        current_u64.saturating_sub(publish_time_u64) <= max_age
+        self.get_current_timestamp()
+            .saturating_sub(publish_time.to::<u64>())
+            <= max_age
     }
+
+    // Stylus doesn't provide a way to mock up the testing timestamp
+    // so at the moment I'm using the testing trait to let me test old timestamps
+    fn get_current_timestamp(&self) -> u64 {
+        #[cfg(test)]
+        {
+            MockClock::time().as_secs()
+        }
+        #[cfg(not(test))]
+        {
+            self.vm().block_timestamp()
+        }
+    }
+}
+
+fn parse_wormhole_proof(vaa: Vaa) -> Result<MerkleRoot<Keccak160>, PythReceiverError> {
+    let message = WormholeMessage::try_from_bytes(vaa.body.payload.to_vec())
+        .map_err(|_| PythReceiverError::PriceUnavailable)?;
+    let root: MerkleRoot<Keccak160> = MerkleRoot::new(match message.payload {
+        WormholePayload::Merkle(merkle_root) => merkle_root.root,
+    });
+    Ok(root)
 }

+ 70 - 6
target_chains/stylus/contracts/pyth-receiver/src/structs.rs

@@ -1,15 +1,40 @@
 use alloc::vec::Vec;
-use stylus_sdk::{prelude::*, storage::{StorageU16, StorageU64, StorageI32, StorageI64, StorageFixedBytes}};
-use stylus_sdk::alloy_primitives::{U64, I32, I64};
+use stylus_sdk::alloy_primitives::{keccak256, FixedBytes, B256, I32, I64, U16, U256, U64};
+use stylus_sdk::{
+    prelude::*,
+    storage::{StorageFixedBytes, StorageI32, StorageI64, StorageKey, StorageU16, StorageU64},
+};
 
-// DataSource struct to store chain/emitter pairs
+fn serialize_data_source_to_bytes(chain_id: u16, emitter_address: &[u8; 32]) -> [u8; 34] {
+    let mut result = [0u8; 34];
+    result[0..2].copy_from_slice(&chain_id.to_be_bytes());
+    result[2..].copy_from_slice(emitter_address);
+    result
+}
+
+#[derive(Debug)]
 #[storage]
 pub struct DataSourceStorage {
     pub chain_id: StorageU16,
     pub emitter_address: StorageFixedBytes<32>,
 }
 
-// PriceInfo struct storing price information
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct DataSource {
+    pub chain_id: U16,
+    pub emitter_address: FixedBytes<32>,
+}
+
+impl StorageKey for DataSource {
+    fn to_slot(&self, root: B256) -> U256 {
+        let chain_id: u16 = self.chain_id.to::<u16>();
+        let emitter_address: [u8; 32] = self.emitter_address.as_slice().try_into().unwrap();
+
+        let bytes = serialize_data_source_to_bytes(chain_id, &emitter_address);
+
+        keccak256(bytes).to_slot(root)
+    }
+}
 #[storage]
 pub struct PriceInfoStorage {
     pub publish_time: StorageU64,
@@ -20,5 +45,44 @@ pub struct PriceInfoStorage {
     pub ema_conf: StorageU64,
 }
 
-// PriceInfo struct storing price information
-pub type PriceInfoReturn = (U64, I32, I64, U64, I64, U64);
+// Addressing nit -- running into some versioning issues that preclude me
+// from returning the PriceInfo struct directly. Need to figure that out.
+
+// pub struct PriceInfo {
+//     pub publish_time: U64,
+//     pub expo: I32,
+//     pub price: I64,
+//     pub conf: U64,
+//     pub ema_price: I64,
+//     pub ema_conf: U64,
+// }
+
+pub type PriceInfoReturn = (U64, I32, I64, U64, I64, U64);
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use stylus_sdk::alloy_primitives::{FixedBytes, U16};
+
+    #[test]
+    fn test_data_source_serialization_compatibility() {
+        let chain_id = 1u16;
+        let emitter_address = [1u8; 32];
+
+        let _data_source = DataSource {
+            chain_id: U16::from(chain_id),
+            emitter_address: FixedBytes::from(emitter_address),
+        };
+
+        let mut expected_bytes = [0u8; 34];
+        expected_bytes[0..2].copy_from_slice(&chain_id.to_be_bytes());
+        expected_bytes[2..].copy_from_slice(&emitter_address);
+
+        let actual_bytes = serialize_data_source_to_bytes(chain_id, &emitter_address);
+
+        assert_eq!(
+            actual_bytes, expected_bytes,
+            "Serialization should produce identical bytes"
+        );
+    }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
target_chains/stylus/contracts/pyth-receiver/src/test_data.rs


+ 6 - 0
target_chains/stylus/contracts/wormhole/Cargo.toml

@@ -23,4 +23,10 @@ motsu.workspace = true
 base64 = "0.21"
 
 [lib]
+name = "wormhole_contract"
 crate-type = ["lib", "cdylib"]
+
+[[bin]]
+name = "wormhole-contract"
+path = "src/main.rs"
+required-features = ["export-abi"]

+ 1 - 8
target_chains/stylus/contracts/wormhole/src/lib.rs

@@ -7,15 +7,9 @@ extern crate alloc;
 #[global_allocator]
 static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
 
-#[cfg(not(any(feature = "std", feature = "export-abi")))]
-#[panic_handler]
-fn panic(_info: &core::panic::PanicInfo) -> ! {
-    loop {}
-}
-
 use alloc::{vec, vec::Vec};
 use stylus_sdk::{
-    prelude::{entrypoint, public, storage},
+    prelude::*,
     storage::{StorageMap, StorageUint, StorageAddress, StorageBool},
     alloy_primitives::{Address, FixedBytes, U256, keccak256},
 };
@@ -320,7 +314,6 @@ impl WormholeContract {
     
 
     fn verify_vm(&self, vaa: &VerifiedVM) -> Result<(), WormholeError> {
-
         let guardian_set = self.get_gs_internal(vaa.guardian_set_index)?;
         if vaa.guardian_set_index != self.current_guardian_set_index.get().try_into().unwrap_or(0u32)
             && guardian_set.expiration_time > 0 {

+ 1 - 0
target_chains/stylus/contracts/wormhole/src/main.rs

@@ -1,4 +1,5 @@
 #![cfg_attr(not(feature = "export-abi"), no_main)]
+#![cfg_attr(not(feature = "export-abi"), no_std)]
 
 #[cfg(feature = "export-abi")]
 fn main() {

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor