소스 검색

feat(target_chains/fuel): add governance contract (#1518)

* add governance contract

* add fuel ci

* add rust-toolchain

* add executes_governance_instruction test

* add test for SetValidPeriod

* add test for AuthorizeGovernanceDataSourceTransfer

* remove SetWormholeAddress

* add test for SetDataSources

* remove WormholeAddressSetEvent

* remove SetWormholeAddress

* remove SetWormholeAddressPayload

* remove SetWormholeAddressPayload and SetWormholeAddress imports

* remove GovernanceAction::SetWormholeAddress

* address comments

* refactor test

* add comments
Daniel Chew 1 년 전
부모
커밋
cf7987f4c5
42개의 변경된 파일1803개의 추가작업 그리고 214개의 파일을 삭제
  1. 35 0
      .github/workflows/ci-fuel-contract.yml
  2. 19 2
      pythnet/pythnet_sdk/src/test_utils/mod.rs
  3. 490 60
      target_chains/fuel/contracts/Cargo.lock
  4. 6 0
      target_chains/fuel/contracts/Cargo.toml
  5. 2 2
      target_chains/fuel/contracts/Forc.lock
  6. 3 3
      target_chains/fuel/contracts/fuel-toolchain.toml
  7. 1 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures.sw
  8. 5 1
      target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw
  9. 183 0
      target_chains/fuel/contracts/pyth-contract/src/data_structures/governance_instruction.sw
  10. 1 11
      target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw
  11. 7 0
      target_chains/fuel/contracts/pyth-contract/src/errors.sw
  12. 26 1
      target_chains/fuel/contracts/pyth-contract/src/events.sw
  13. 308 36
      target_chains/fuel/contracts/pyth-contract/src/main.sw
  14. 2 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures.sw
  15. 29 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_instruction.sw
  16. 29 0
      target_chains/fuel/contracts/pyth-interface/src/data_structures/governance_payload.sw
  17. 0 5
      target_chains/fuel/contracts/pyth-interface/src/data_structures/wormhole_light.sw
  18. 8 4
      target_chains/fuel/contracts/pyth-interface/src/interface.sw
  19. 27 4
      target_chains/fuel/contracts/scripts/deploy_pyth.rs
  20. 22 1
      target_chains/fuel/contracts/src/constants.rs
  21. 148 2
      target_chains/fuel/contracts/src/pyth_utils.rs
  22. 1 0
      target_chains/fuel/contracts/tests/functions/mod.rs
  23. 15 6
      target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs
  24. 14 5
      target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs
  25. 15 6
      target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs
  26. 14 5
      target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs
  27. 15 7
      target_chains/fuel/contracts/tests/functions/pyth_core/price.rs
  28. 14 5
      target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs
  29. 14 5
      target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs
  30. 15 4
      target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs
  31. 15 5
      target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs
  32. 15 4
      target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs
  33. 229 0
      target_chains/fuel/contracts/tests/functions/pyth_governance/execute_governance_instruction.rs
  34. 1 0
      target_chains/fuel/contracts/tests/functions/pyth_governance/mod.rs
  35. 14 5
      target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs
  36. 16 17
      target_chains/fuel/contracts/tests/functions/pyth_init/constructor.rs
  37. 1 1
      target_chains/fuel/contracts/tests/functions/pyth_init/mod.rs
  38. 1 0
      target_chains/fuel/contracts/tests/utils/interface/mod.rs
  39. 27 0
      target_chains/fuel/contracts/tests/utils/interface/pyth_governance.rs
  40. 2 2
      target_chains/fuel/contracts/tests/utils/interface/pyth_info.rs
  41. 12 3
      target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs
  42. 2 2
      target_chains/fuel/contracts/tests/utils/interface/wormhole_guardians.rs

+ 35 - 0
.github/workflows/ci-fuel-contract.yml

@@ -0,0 +1,35 @@
+name: Test Fuel Contract
+
+on:
+  pull_request:
+    paths:
+      - target_chains/fuel/**
+  push:
+    branches:
+      - main
+    paths:
+      - target_chains/fuel/**
+
+env:
+  CARGO_TERM_COLOR: always
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: target_chains/fuel/contracts/
+    steps:
+      - uses: actions/checkout@v2
+      - name: Install Fuel toolchain
+        run: |
+          curl https://install.fuel.network | sh
+          echo "$HOME/.fuelup/bin" >> $GITHUB_PATH
+      - name: Build with Forc
+        run: forc build --verbose
+      - name: Run tests with Forc
+        run: forc test --verbose
+      - name: Build
+        run: cargo build --verbose
+      - name: Run tests
+        run: cargo test --verbose

+ 19 - 2
pythnet/pythnet_sdk/src/test_utils/mod.rs

@@ -4,7 +4,11 @@ use {
             merkle::MerkleTree,
             Accumulator,
         },
-        hashers::keccak256_160::Keccak160,
+        hashers::{
+            keccak256::Keccak256,
+            keccak256_160::Keccak160,
+            Hasher,
+        },
         messages::{
             FeedId,
             Message,
@@ -27,6 +31,7 @@ use {
     byteorder::BigEndian,
     libsecp256k1::{
         Message as libsecp256k1Message,
+        PublicKey,
         RecoveryId,
         SecretKey,
         Signature,
@@ -96,6 +101,19 @@ pub fn dummy_guardians() -> Vec<SecretKey> {
     result
 }
 
+pub fn dummy_guardians_addresses() -> Vec<[u8; 20]> {
+    let guardians = dummy_guardians();
+    guardians
+        .iter()
+        .map(|x| {
+            let mut result: [u8; 20] = [0u8; 20];
+            let pubkey = &PublicKey::from_secret_key(x).serialize()[1..];
+            result.copy_from_slice(&Keccak256::hashv(&[&pubkey])[12..]);
+            result
+        })
+        .collect()
+}
+
 pub fn create_dummy_feed_id(value: i64) -> FeedId {
     let mut dummy_id = [0; 32];
     dummy_id[0] = value as u8;
@@ -271,7 +289,6 @@ pub fn create_vaa_from_payload(
         ..Default::default()
     };
 
-
     (header, body).into()
 }
 

+ 490 - 60
target_chains/fuel/contracts/Cargo.lock

@@ -134,6 +134,12 @@ version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
 
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
 [[package]]
 name = "ascii"
 version = "0.9.3"
@@ -182,7 +188,7 @@ dependencies = [
  "Inflector",
  "async-graphql-parser",
  "darling 0.14.4",
- "proc-macro-crate",
+ "proc-macro-crate 1.3.1",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
@@ -360,6 +366,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 = "bitflags"
 version = "1.3.2"
@@ -387,6 +402,15 @@ dependencies = [
  "wyz",
 ]
 
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 name = "block-buffer"
 version = "0.10.4"
@@ -396,22 +420,98 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "borsh"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
+dependencies = [
+ "borsh-derive",
+ "hashbrown 0.13.2",
+]
+
+[[package]]
+name = "borsh-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7"
+dependencies = [
+ "borsh-derive-internal",
+ "borsh-schema-derive-internal",
+ "proc-macro-crate 0.1.5",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-derive-internal"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "borsh-schema-derive-internal"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "bs58"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
 dependencies = [
- "sha2",
+ "sha2 0.10.8",
  "tinyvec",
 ]
 
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
 [[package]]
 name = "bumpalo"
 version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
+[[package]]
+name = "bytemuck"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
 [[package]]
 name = "byteorder"
 version = "1.5.0"
@@ -517,11 +617,11 @@ checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3"
 dependencies = [
  "bs58",
  "coins-core",
- "digest",
- "hmac",
+ "digest 0.10.7",
+ "hmac 0.12.1",
  "k256",
  "serde",
- "sha2",
+ "sha2 0.10.8",
  "thiserror",
 ]
 
@@ -533,11 +633,11 @@ checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528"
 dependencies = [
  "bitvec",
  "coins-bip32",
- "hmac",
+ "hmac 0.12.1",
  "once_cell",
  "pbkdf2 0.12.2",
  "rand",
- "sha2",
+ "sha2 0.10.8",
  "thiserror",
 ]
 
@@ -550,13 +650,13 @@ dependencies = [
  "base64 0.21.7",
  "bech32",
  "bs58",
- "digest",
+ "digest 0.10.7",
  "generic-array",
  "hex",
  "ripemd",
  "serde",
  "serde_derive",
- "sha2",
+ "sha2 0.10.8",
  "sha3",
  "thiserror",
 ]
@@ -713,6 +813,16 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "crypto-mac"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
 [[package]]
 name = "ct-logs"
 version = "0.8.0"
@@ -740,7 +850,7 @@ dependencies = [
  "cfg-if",
  "cpufeatures",
  "curve25519-dalek-derive",
- "digest",
+ "digest 0.10.7",
  "fiat-crypto",
  "platforms",
  "rustc_version",
@@ -911,13 +1021,22 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 name = "digest"
 version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
- "block-buffer",
+ "block-buffer 0.10.4",
  "const-oid",
  "crypto-common",
  "subtle",
@@ -935,6 +1054,12 @@ version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
 
+[[package]]
+name = "dyn-clone"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+
 [[package]]
 name = "ecdsa"
 version = "0.16.9"
@@ -942,7 +1067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
 dependencies = [
  "der",
- "digest",
+ "digest 0.10.7",
  "elliptic-curve",
  "rfc6979",
  "signature",
@@ -966,7 +1091,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
 dependencies = [
  "curve25519-dalek",
  "ed25519",
- "sha2",
+ "sha2 0.10.8",
  "subtle",
 ]
 
@@ -984,7 +1109,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
 dependencies = [
  "base16ct",
  "crypto-bigint",
- "digest",
+ "digest 0.10.7",
  "ff",
  "generic-array",
  "group",
@@ -1054,15 +1179,15 @@ checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab"
 dependencies = [
  "aes",
  "ctr",
- "digest",
+ "digest 0.10.7",
  "hex",
- "hmac",
+ "hmac 0.12.1",
  "pbkdf2 0.11.0",
  "rand",
  "scrypt",
  "serde",
  "serde_json",
- "sha2",
+ "sha2 0.10.8",
  "sha3",
  "thiserror",
  "uuid 0.8.2",
@@ -1089,11 +1214,20 @@ dependencies = [
  "tokio",
 ]
 
+[[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.0.2"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
 
 [[package]]
 name = "ff"
@@ -1451,7 +1585,7 @@ dependencies = [
  "rand",
  "secp256k1",
  "serde",
- "sha2",
+ "sha2 0.10.8",
  "zeroize",
 ]
 
@@ -1474,12 +1608,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "89143dd80b29dda305fbb033bc7f868834445ef6b361bf920f0077938fb6c0bc"
 dependencies = [
  "derive_more",
- "digest",
+ "digest 0.10.7",
  "fuel-storage",
  "hashbrown 0.13.2",
  "hex",
  "serde",
- "sha2",
+ "sha2 0.10.8",
 ]
 
 [[package]]
@@ -1633,7 +1767,7 @@ dependencies = [
  "itertools 0.12.1",
  "serde",
  "serde_json",
- "sha2",
+ "sha2 0.10.8",
  "thiserror",
  "uint",
 ]
@@ -1694,7 +1828,7 @@ dependencies = [
  "rand",
  "serde",
  "serde_json",
- "serde_with 3.7.0",
+ "serde_with 3.8.0",
  "tempfile",
  "tokio",
  "which",
@@ -1939,13 +2073,34 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "hmac"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840"
+dependencies = [
+ "crypto-mac",
+ "digest 0.9.0",
+]
+
 [[package]]
 name = "hmac"
 version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
 dependencies = [
- "digest",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "hmac-drbg"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1"
+dependencies = [
+ "digest 0.9.0",
+ "generic-array",
+ "hmac 0.8.1",
 ]
 
 [[package]]
@@ -2048,7 +2203,7 @@ dependencies = [
  "http",
  "hyper",
  "log",
- "rustls 0.21.11",
+ "rustls 0.21.12",
  "rustls-native-certs 0.6.3",
  "tokio",
  "tokio-rustls 0.24.1",
@@ -2129,6 +2284,12 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "ieee754"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c"
+
 [[package]]
 name = "indexmap"
 version = "1.9.3"
@@ -2208,7 +2369,7 @@ dependencies = [
  "ecdsa",
  "elliptic-curve",
  "once_cell",
- "sha2",
+ "sha2 0.10.8",
  "signature",
 ]
 
@@ -2239,6 +2400,54 @@ version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
+[[package]]
+name = "libsecp256k1"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1"
+dependencies = [
+ "arrayref",
+ "base64 0.13.1",
+ "digest 0.9.0",
+ "hmac-drbg",
+ "libsecp256k1-core",
+ "libsecp256k1-gen-ecmult",
+ "libsecp256k1-gen-genmult",
+ "rand",
+ "serde",
+ "sha2 0.9.9",
+ "typenum",
+]
+
+[[package]]
+name = "libsecp256k1-core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451"
+dependencies = [
+ "crunchy",
+ "digest 0.9.0",
+ "subtle",
+]
+
+[[package]]
+name = "libsecp256k1-gen-ecmult"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809"
+dependencies = [
+ "libsecp256k1-core",
+]
+
+[[package]]
+name = "libsecp256k1-gen-genmult"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c"
+dependencies = [
+ "libsecp256k1-core",
+]
+
 [[package]]
 name = "linux-raw-sys"
 version = "0.4.13"
@@ -2247,9 +2456,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
 [[package]]
 name = "lock_api"
-version = "0.4.11"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
 dependencies = [
  "autocfg",
  "scopeguard",
@@ -2335,12 +2544,78 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "num"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "num-conv"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
 
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.2.18"
@@ -2375,6 +2650,12 @@ version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
+[[package]]
+name = "opaque-debug"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
 [[package]]
 name = "openssl"
 version = "0.10.64"
@@ -2428,14 +2709,14 @@ dependencies = [
  "ecdsa",
  "elliptic-curve",
  "primeorder",
- "sha2",
+ "sha2 0.10.8",
 ]
 
 [[package]]
 name = "parking_lot"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
 dependencies = [
  "lock_api",
  "parking_lot_core",
@@ -2443,15 +2724,15 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.9"
+version = "0.9.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -2466,7 +2747,7 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
 dependencies = [
- "digest",
+ "digest 0.10.7",
 ]
 
 [[package]]
@@ -2475,8 +2756,8 @@ version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
 dependencies = [
- "digest",
- "hmac",
+ "digest 0.10.7",
+ "hmac 0.12.1",
 ]
 
 [[package]]
@@ -2602,6 +2883,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",
+]
+
 [[package]]
 name = "proc-macro-crate"
 version = "1.3.1"
@@ -2691,11 +2981,37 @@ dependencies = [
  "dotenv",
  "fuels",
  "hex",
+ "libsecp256k1",
+ "pythnet-sdk",
  "rand",
  "reqwest",
  "serde",
  "serde_json",
+ "serde_wormhole",
+ "sha3",
  "tokio",
+ "wormhole-vaas-serde",
+]
+
+[[package]]
+name = "pythnet-sdk"
+version = "2.0.0"
+dependencies = [
+ "bincode",
+ "borsh",
+ "bytemuck",
+ "byteorder",
+ "fast-math",
+ "hex",
+ "libsecp256k1",
+ "rand",
+ "rustc_version",
+ "serde",
+ "serde_wormhole",
+ "sha3",
+ "slow_primes",
+ "thiserror",
+ "wormhole-vaas-serde",
 ]
 
 [[package]]
@@ -2765,11 +3081,11 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.5.0",
 ]
 
 [[package]]
@@ -2828,7 +3144,7 @@ dependencies = [
  "once_cell",
  "percent-encoding",
  "pin-project-lite",
- "rustls 0.21.11",
+ "rustls 0.21.12",
  "rustls-pemfile",
  "serde",
  "serde_json",
@@ -2853,7 +3169,7 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
 dependencies = [
- "hmac",
+ "hmac 0.12.1",
  "subtle",
 ]
 
@@ -2893,7 +3209,7 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
 dependencies = [
- "digest",
+ "digest 0.10.7",
 ]
 
 [[package]]
@@ -2913,9 +3229,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.33"
+version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
  "bitflags 2.5.0",
  "errno",
@@ -2939,9 +3255,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.21.11"
+version = "0.21.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
+checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
 dependencies = [
  "log",
  "ring 0.17.8",
@@ -3048,6 +3364,30 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "schemars"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
+dependencies = [
+ "dyn-clone",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -3060,10 +3400,10 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d"
 dependencies = [
- "hmac",
+ "hmac 0.12.1",
  "pbkdf2 0.11.0",
  "salsa20",
- "sha2",
+ "sha2 0.10.8",
 ]
 
 [[package]]
@@ -3159,24 +3499,44 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
 
 [[package]]
 name = "serde"
-version = "1.0.198"
+version = "1.0.199"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
+checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a"
 dependencies = [
  "serde_derive",
 ]
 
+[[package]]
+name = "serde_bytes"
+version = "0.11.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "serde_derive"
-version = "1.0.198"
+version = "1.0.199"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
+checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.60",
 ]
 
+[[package]]
+name = "serde_derive_internals"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "serde_json"
 version = "1.0.116"
@@ -3212,9 +3572,9 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "3.7.0"
+version = "3.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
+checksum = "2c85f8e96d1d6857f13768fcbd895fcb06225510022a2774ed8b5150581847b0"
 dependencies = [
  "serde",
  "serde_derive",
@@ -3232,6 +3592,32 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "serde_wormhole"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24b022bf813578a06341fd453c3fd6e64945d9975191193d5d45e8dbd97d1d84"
+dependencies = [
+ "base64 0.13.1",
+ "itoa",
+ "serde",
+ "serde_bytes",
+ "thiserror",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.8"
@@ -3240,7 +3626,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 dependencies = [
  "cfg-if",
  "cpufeatures",
- "digest",
+ "digest 0.10.7",
 ]
 
 [[package]]
@@ -3249,7 +3635,7 @@ version = "0.10.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
 dependencies = [
- "digest",
+ "digest 0.10.7",
  "keccak",
 ]
 
@@ -3268,7 +3654,7 @@ version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
 dependencies = [
- "digest",
+ "digest 0.10.7",
  "rand_core",
 ]
 
@@ -3281,6 +3667,15 @@ dependencies = [
  "autocfg",
 ]
 
+[[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.13.2"
@@ -3604,7 +3999,7 @@ version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
 dependencies = [
- "rustls 0.21.11",
+ "rustls 0.21.12",
  "tokio",
 ]
 
@@ -3634,6 +4029,15 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "toml_datetime"
 version = "0.6.5"
@@ -4179,6 +4583,32 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "wormhole-supported-chains"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f42a80a24212937cc7d7b0ab8115bb87d82f949a1a42f75d500807072c94ba4"
+dependencies = [
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "wormhole-vaas-serde"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240c5a6136dc66ecc65097bb6d159e849b5df4ecbbbb220868d0edbdcc568ed3"
+dependencies = [
+ "anyhow",
+ "bstr",
+ "schemars",
+ "serde",
+ "serde_wormhole",
+ "sha3",
+ "thiserror",
+ "wormhole-supported-chains",
+]
+
 [[package]]
 name = "wyz"
 version = "0.5.1"

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

@@ -16,6 +16,12 @@ reqwest = "0.11.27"
 serde_json = "1.0.114"
 serde = "1.0.197"
 dotenv = "0.15.0"
+libsecp256k1 = "0.7.1"
+pythnet-sdk = { path = "../../../pythnet/pythnet_sdk", features = ["test-utils"] }
+sha3 = "0.10.8"
+serde_wormhole = { version ="0.1.0" }
+wormhole-vaas-serde = { version = "0.1.0" }
+
 
 [[bin]]
 name = "deploy_pyth"

+ 2 - 2
target_chains/fuel/contracts/Forc.lock

@@ -1,6 +1,6 @@
 [[package]]
 name = "core"
-source = "path+from-root-C3992B43B72ADB8C"
+source = "path+from-root-566CA1D5F8BEAFBF"
 
 [[package]]
 name = "ownership"
@@ -40,5 +40,5 @@ dependencies = ["std"]
 
 [[package]]
 name = "std"
-source = "git+https://github.com/fuellabs/sway?tag=v0.49.1#2ac7030570f22510b0ac2a7b5ddf7baa20bdc0e1"
+source = "git+https://github.com/fuellabs/sway?tag=v0.49.3#0dc6570377ee9c4a6359ade597fa27351e02a728"
 dependencies = ["core"]

+ 3 - 3
target_chains/fuel/contracts/fuel-toolchain.toml

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

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

@@ -6,3 +6,4 @@ pub mod price;
 pub mod accumulator_update;
 pub mod batch_attestation_update;
 pub mod update_type;
+pub mod governance_instruction;

+ 5 - 1
target_chains/fuel/contracts/pyth-contract/src/data_structures/data_source.sw

@@ -19,7 +19,7 @@ impl DataSource {
     }
 
     #[storage(read)]
-    pub fn is_valid(
+    pub fn is_valid_data_source(
         self,
         is_valid_data_source: StorageKey<StorageMap<DataSource, bool>>,
 ) -> bool {
@@ -28,4 +28,8 @@ impl DataSource {
             None => false,
         }
     }
+
+    pub fn is_valid_governance_data_source(self, chain_id: u16, emitter_address: b256) -> bool {
+        self.chain_id == chain_id && self.emitter_address == emitter_address
+    }
 }

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

@@ -0,0 +1,183 @@
+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}, governance_payload::{UpgradeContractPayload, AuthorizeGovernanceDataSourceTransferPayload, RequestGovernanceDataSourceTransferPayload, SetDataSourcesPayload, SetFeePayload, SetValidPeriodPayload}, governance_instruction::{GovernanceInstruction, GovernanceModule, GovernanceAction}};
+use std::{bytes::Bytes, hash::Hash};
+use std::math::*;
+use std::primitive_conversions::{u32::*, u64::*};
+
+
+pub const MAGIC: u32 = 0x5054474d;
+
+impl GovernanceInstruction {
+    pub fn new(magic: u32,
+                module: GovernanceModule,
+                action: GovernanceAction,
+                target_chain_id: u16,
+                payload: Bytes
+                ) -> Self {
+        Self { magic, module, action, target_chain_id, payload }
+    }
+
+    pub fn parse_governance_instruction(encoded_instruction: Bytes) -> Self {
+        let mut index = 0;
+        let magic = u32::from_be_bytes([
+            encoded_instruction.get(index).unwrap(),
+            encoded_instruction.get(index + 1).unwrap(),
+            encoded_instruction.get(index + 2).unwrap(),
+            encoded_instruction.get(index + 3).unwrap(),
+        ]);
+        require(magic == MAGIC, PythError::InvalidMagic);
+        index += 4;
+
+        let mod_number = encoded_instruction.get(index).unwrap();
+        let module = match mod_number {
+            0 => GovernanceModule::Executor,
+            1 => GovernanceModule::Target,
+            2 => GovernanceModule::EvmExecutor,
+            3 => GovernanceModule::StacksTarget,
+            _ => GovernanceModule::Invalid,
+        };
+        require(match module {
+            GovernanceModule::Target => true,
+            _ => false,
+        }, PythError::InvalidGovernanceTarget);
+        index += 1;
+
+        let action_number = encoded_instruction.get(index).unwrap();
+        let governance_action = match action_number {
+            0 => GovernanceAction::UpgradeContract, // Not implemented
+            1 => GovernanceAction::AuthorizeGovernanceDataSourceTransfer,
+            2 => GovernanceAction::SetDataSources,
+            3 => GovernanceAction::SetFee,
+            4 => GovernanceAction::SetValidPeriod,
+            5 => GovernanceAction::RequestGovernanceDataSourceTransfer,
+            _ => GovernanceAction::Invalid,
+        };
+        require(match governance_action {
+            GovernanceAction::Invalid => false,
+            _ => true,
+        }, PythError::InvalidGovernanceAction);
+        index += 1;
+
+        let target_chain_id = u16::from_be_bytes([
+            encoded_instruction.get(index).unwrap(),
+            encoded_instruction.get(index + 1).unwrap(),
+        ]);
+        index += 2;
+
+        let (_, payload) = encoded_instruction.split_at(index);
+
+        GovernanceInstruction::new(
+            magic,
+            module,
+            governance_action,
+            target_chain_id,
+            payload,
+        )
+    }
+
+    /// Parse an AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
+    pub fn parse_authorize_governance_data_source_transfer_payload(encoded_payload: Bytes) -> AuthorizeGovernanceDataSourceTransferPayload {
+        AuthorizeGovernanceDataSourceTransferPayload {
+            claim_vaa: encoded_payload,
+        }
+    }
+
+    pub fn parse_request_governance_data_source_transfer_payload(encoded_payload: Bytes) -> RequestGovernanceDataSourceTransferPayload {
+        let mut index = 0;
+        let governance_data_source_index = 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(),
+        ]);
+        index += 4;
+        require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage);
+        let rdgst = RequestGovernanceDataSourceTransferPayload {
+            governance_data_source_index,
+        };
+        rdgst
+    }
+
+    pub fn parse_set_data_sources_payload(encoded_payload: Bytes) -> SetDataSourcesPayload {
+        let mut index = 0;
+        let data_sources_length = encoded_payload.get(index).unwrap().as_u64();
+        index += 1;
+        let mut data_sources = Vec::with_capacity(data_sources_length);
+
+        let mut i = 0;
+        while i < data_sources_length {
+            let (_, slice) = encoded_payload.split_at(index);
+            let (slice, _) = slice.split_at(2);
+            let chain_id = u16::from_be_bytes([slice.get(0).unwrap(), slice.get(1).unwrap()]);
+            index += 2;
+            let (_, slice) = encoded_payload.split_at(index);
+            let (slice, _) = slice.split_at(32);
+            let emitter_address: b256 = slice.into();
+            index += 32;
+
+            data_sources.push(DataSource {
+                chain_id,
+                emitter_address,
+            });
+            i += 1
+        }
+
+        require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage);
+        let sds = SetDataSourcesPayload { data_sources };
+        sds
+    }
+
+    pub fn parse_set_fee_payload(encoded_payload: Bytes) -> SetFeePayload {
+        let mut index = 0;
+        let val = u64::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(),
+            encoded_payload.get(index + 4).unwrap(),
+            encoded_payload.get(index + 5).unwrap(),
+            encoded_payload.get(index + 6).unwrap(),
+            encoded_payload.get(index + 7).unwrap(),
+        ]);
+        index += 8;
+        let expo = u64::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(),
+            encoded_payload.get(index + 4).unwrap(),
+            encoded_payload.get(index + 5).unwrap(),
+            encoded_payload.get(index + 6).unwrap(),
+            encoded_payload.get(index + 7).unwrap(),
+        ]);
+        index += 8;
+        require(encoded_payload.len() == index, PythError::InvalidGovernanceMessage);
+        let sf = SetFeePayload {
+            new_fee: val * 10u64.pow(expo.try_as_u32().unwrap()),
+        };
+        sf
+    }
+
+    pub fn parse_set_valid_period_payload(encoded_payload: Bytes) -> SetValidPeriodPayload {
+        let mut index = 0;
+        let valid_time_period_seconds = u64::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(),
+            encoded_payload.get(index + 4).unwrap(),
+            encoded_payload.get(index + 5).unwrap(),
+            encoded_payload.get(index + 6).unwrap(),
+            encoded_payload.get(index + 7).unwrap(),
+        ]);
+        index += 8;
+        require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage);
+        let svp = SetValidPeriodPayload {
+            new_valid_period: valid_time_period_seconds,
+        };
+        svp
+    }
+}

+ 1 - 11
target_chains/fuel/contracts/pyth-contract/src/data_structures/wormhole_light.sw

@@ -6,7 +6,6 @@ use pyth_interface::data_structures::{
     data_source::DataSource,
     wormhole_light::{
         GuardianSet,
-        WormholeProvider,
     },
 };
 use std::{
@@ -145,15 +144,6 @@ impl GuardianSetUpgrade {
     }
 }
 
-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,
@@ -582,7 +572,7 @@ impl WormholeVM {
         );
         require(
             DataSource::new(vm.emitter_chain_id, vm.emitter_address)
-                .is_valid(is_valid_data_source),
+                .is_valid_data_source(is_valid_data_source),
             WormholeError::InvalidUpdateDataSource,
         );
         vm

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

@@ -9,6 +9,11 @@ pub enum PythError {
     InvalidAttestationSize: (),
     InvalidDataSourcesLength: (),
     InvalidExponent: (),
+    InvalidGovernanceDataSource: (),
+    InvalidGovernanceAction: (),
+    InvalidGovernanceMessage: (),
+    InvalidGovernanceModule: (),
+    InvalidGovernanceTarget: (),
     InvalidHeaderSize: (),
     InvalidMagic: (),
     InvalidMajorVersion: (),
@@ -21,9 +26,11 @@ pub enum PythError {
     InvalidUpdateDataLength: (),
     InvalidUpdateDataSource: (),
     InvalidUpgradeModule: (),
+    InvalidWormholeAddressToSet: (),
     LengthOfPriceFeedIdsAndPublishTimesMustMatch: (),
     NewGuardianSetIsEmpty: (),
     NumberOfUpdatesIrretrievable: (),
+    OldGovernanceMessage: (),
     /// Emitted when a Price's `publish_time` is stale.
     OutdatedPrice: (),
     /// Emitted when a PriceFeed could not be retrieved.

+ 26 - 1
target_chains/fuel/contracts/pyth-contract/src/events.sw

@@ -3,7 +3,6 @@ library;
 use pyth_interface::data_structures::{
     data_source::DataSource,
     price::PriceFeedId,
-    wormhole_light::WormholeProvider,
 };
 
 pub struct ConstructedEvent {
@@ -19,3 +18,29 @@ pub struct NewGuardianSetEvent {
 pub struct UpdatedPriceFeedsEvent {
     updated_price_feeds: Vec<PriceFeedId>,
 }
+
+pub struct ContractUpgradedEvent {
+    old_implementation: Identity,
+    new_implementation: Identity,
+}
+
+pub struct GovernanceDataSourceSetEvent {
+    old_data_source: DataSource,
+    new_data_source: DataSource,
+    initial_sequence: u64,
+}
+
+pub struct DataSourcesSetEvent {
+    old_data_sources: Vec<DataSource>,
+    new_data_sources: Vec<DataSource>,
+}
+
+pub struct FeeSetEvent {
+    old_fee: u64,
+    new_fee: u64,
+}
+
+pub struct ValidPeriodSetEvent {
+    old_valid_period: u64,
+    new_valid_period: u64,
+}

+ 308 - 36
target_chains/fuel/contracts/pyth-contract/src/main.sw

@@ -15,12 +15,17 @@ use std::{
         ZERO_B256,
     },
     context::msg_amount,
-    hash::Hash,
+    hash::{
+        Hash,
+        keccak256,
+        sha256,
+    },
     storage::{
         storage_map::StorageMap,
         storage_vec::*,
     },
     u256::U256,
+    revert::revert,
 };
 
 use ::errors::{PythError, WormholeError};
@@ -31,8 +36,9 @@ use ::data_structures::{
     price::*,
     update_type::UpdateType,
     wormhole_light::*,
+    governance_instruction::*,
 };
-use ::events::{ConstructedEvent, NewGuardianSetEvent, UpdatedPriceFeedsEvent};
+use ::events::{ConstructedEvent, NewGuardianSetEvent, UpdatedPriceFeedsEvent, ContractUpgradedEvent, GovernanceDataSourceSetEvent, DataSourcesSetEvent, FeeSetEvent, ValidPeriodSetEvent};
 
 use pyth_interface::{
     data_structures::{
@@ -42,10 +48,11 @@ use pyth_interface::{
             PriceFeed,
             PriceFeedId,
         },
+        governance_payload::{UpgradeContractPayload, AuthorizeGovernanceDataSourceTransferPayload, SetDataSourcesPayload, SetFeePayload, SetValidPeriodPayload},
         wormhole_light::{
             GuardianSet,
-            WormholeProvider,
         },
+        governance_instruction::{GovernanceInstruction, GovernanceModule, GovernanceAction},
     },
     PythCore,
     PythInfo,
@@ -70,6 +77,7 @@ storage {
     // Mapping of cached price information
     // priceId => PriceInfo
     latest_price_feed: StorageMap<PriceFeedId, PriceFeed> = StorageMap {},
+    // Fee required for each update
     single_update_fee: u64 = 0,
     // For tracking all active emitter/chain ID pairs
     valid_data_sources: StorageVec<DataSource> = StorageVec {},
@@ -77,20 +85,38 @@ storage {
     /// 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
+    /// Governance data source. VAA messages from this source can change this contract
+    /// state. e.g., upgrade the contract, change the valid data sources, and more.
+    governance_data_source: DataSource = DataSource {
+        chain_id: 0u16,
+        emitter_address: ZERO_B256,
+    },
+    /// Index of the governance data source, increased each time the governance data source changes.
+    governance_data_source_index: u32 = 0,
+    /// Sequence number of the last executed governance message. Any governance message
+    /// with a lower or equal sequence number will be discarded. This prevents double-execution,
+    /// and also makes sure that messages are executed in the right order.
+    last_executed_governance_sequence: u64 = 0,
+    /// Chain ID of the contract
+    chain_id: u16 = 0,
+    ///   |                    |
+    /// --+-- WORMHOLE STATE --+--
+    ///   |                    |
+    /// Mapping of consumed governance actions
     wormhole_consumed_governance_actions: StorageMap<b256, bool> = StorageMap {},
-    // Mapping of guardian_set_index => guardian set
+    /// Mapping of guardian_set_index => guardian set
     wormhole_guardian_sets: StorageMap<u32, StorageGuardianSet> = StorageMap {},
-    // Current active guardian set index
+    /// 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,
+    /// Using Ethereum's Wormhole governance
+    wormhole_governance_data_source: DataSource = DataSource {
+        chain_id: 0u16,
+        emitter_address: ZERO_B256,
     },
+    ///   |                    |
+    /// --+-- GOVERNANCE STATE --+--
+    ///   |                    |
+    current_implementation: Identity = Identity::Address(Address::from(ZERO_B256)),
 }
 
 impl SRC5 for Contract {
@@ -409,15 +435,61 @@ fn valid_time_period() -> u64 {
     storage.valid_time_period_seconds.read()
 }
 
+#[storage(read)]
+fn governance_data_source() -> DataSource {
+    storage.governance_data_source.read()
+}
+
+#[storage(write)]
+fn set_governance_data_source(data_source: DataSource) {
+    storage.governance_data_source.write(data_source);
+}
+
+#[storage(read)]
+fn governance_data_source_index() -> u32 {
+    storage.governance_data_source_index.read()
+}
+
+#[storage(write)]
+fn set_governance_data_source_index(index: u32) {
+    storage.governance_data_source_index.write(index);
+}
+
+#[storage(read)]
+fn last_executed_governance_sequence() -> u64 {
+    storage.last_executed_governance_sequence.read()
+}
+
+#[storage(write)]
+fn set_last_executed_governance_sequence(sequence: u64) {
+    storage.last_executed_governance_sequence.write(sequence);
+}
+
+#[storage(read)]
+fn chain_id() -> u16 {
+    storage.chain_id.read()
+}
+
+#[storage(read)]
+fn current_implementation() -> Identity {
+    storage.current_implementation.read()
+}
+
 impl PythInit for Contract {
     #[storage(read, write)]
     fn constructor(
         data_sources: Vec<DataSource>,
+        governance_data_source: DataSource,
+        wormhole_governance_data_source: DataSource,
         single_update_fee: u64,
         valid_time_period_seconds: u64,
-        wormhole_guardian_set_upgrade: Bytes,
+        wormhole_guardian_set_addresses: Vec<b256>,
+        wormhole_guardian_set_index: u32,
+        chain_id: u16,
     ) {
+        // This function sets the passed identity as the initial owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L127-L138
         initialize_ownership(DEPLOYER);
+        // This function ensures that the sender is the owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L59-L65
         only_owner();
 
         require(data_sources.len > 0, PythError::InvalidDataSourcesLength);
@@ -436,26 +508,35 @@ impl PythInit for Contract {
             .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);
+        let guardian_length: u8 = wormhole_guardian_set_addresses.len().try_as_u8().unwrap();
+        let mut new_guardian_set = StorageGuardianSet::new(
+            0,
+            StorageKey {
+                slot: sha256(("guardian_set_keys", wormhole_guardian_set_index)),
+                offset: 0,
+                field_id: ZERO_B256,
+            },
+        );
+        let mut i: u8 = 0;
+        while i < guardian_length {
+            let key: b256 = wormhole_guardian_set_addresses.get(i.as_u64()).unwrap();
+            new_guardian_set.keys.push(key);
+            i += 1;
+        }
+
+        storage.wormhole_guardian_set_index.write(wormhole_guardian_set_index);
+        storage.wormhole_guardian_sets.insert(wormhole_guardian_set_index, new_guardian_set);
 
-        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));
+        storage.governance_data_source.write(governance_data_source);
+        storage.wormhole_governance_data_source.write(wormhole_governance_data_source);
+
+        storage.chain_id.write(chain_id);
 
+        // This function revokes ownership of the current owner and disallows any new owners. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L89-L99
         renounce_ownership();
 
         log(ConstructedEvent {
-            guardian_set_index: upgrade.new_guardian_set_index,
+            guardian_set_index: wormhole_guardian_set_index,
         })
     }
 }
@@ -492,8 +573,8 @@ impl PythInfo for Contract {
     }
 
     #[storage(read)]
-    fn valid_data_source(data_source: DataSource) -> bool {
-        data_source.is_valid(storage.is_valid_data_source)
+    fn is_valid_data_source(data_source: DataSource) -> bool {
+        data_source.is_valid_data_source(storage.is_valid_data_source)
     }
 }
 
@@ -513,7 +594,7 @@ impl WormholeGuardians for Contract {
     }
 
     #[storage(read)]
-    fn current_wormhole_provider() -> WormholeProvider {
+    fn current_wormhole_provider() -> DataSource {
         current_wormhole_provider()
     }
 
@@ -546,8 +627,8 @@ fn current_guardian_set_index() -> u32 {
 }
 
 #[storage(read)]
-fn current_wormhole_provider() -> WormholeProvider {
-    storage.wormhole_provider.read()
+fn current_wormhole_provider() -> DataSource {
+    storage.wormhole_governance_data_source.read()
 }
 
 #[storage(read)]
@@ -573,12 +654,12 @@ fn submit_new_guardian_set(encoded_vm: Bytes) {
     let current_wormhole_provider = current_wormhole_provider();
     require(
         vm.emitter_chain_id == current_wormhole_provider
-            .governance_chain_id,
+            .chain_id,
         WormholeError::InvalidGovernanceChain,
     );
     require(
         vm.emitter_address == current_wormhole_provider
-            .governance_contract,
+            .emitter_address,
         WormholeError::InvalidGovernanceContract,
     );
     require(
@@ -615,3 +696,194 @@ fn submit_new_guardian_set(encoded_vm: Bytes) {
         new_guardian_set_index: upgrade.new_guardian_set_index,
     })
 }
+
+/// Transfer the governance data source to a new value with sanity checks to ensure the new governance data source can manage the contract.
+#[storage(read, write)]
+fn authorize_governance_data_source_transfer(payload: AuthorizeGovernanceDataSourceTransferPayload) {
+    let old_governance_data_source = governance_data_source();
+
+    // Parse and verify the VAA contained in the payload to ensure it's valid and can manage the contract
+    let vm = WormholeVM::parse_and_verify_wormhole_vm(
+        current_guardian_set_index(),
+        payload.claim_vaa,
+        storage.wormhole_guardian_sets,
+    );
+
+    let gi = GovernanceInstruction::parse_governance_instruction(vm.payload);
+    require(gi.target_chain_id == chain_id() || gi.target_chain_id == 0, PythError::InvalidGovernanceTarget);
+
+    require(match gi.action {
+        GovernanceAction::RequestGovernanceDataSourceTransfer => true,
+        _ => false,
+    }, PythError::InvalidGovernanceMessage);
+
+    let claim_payload = GovernanceInstruction::parse_request_governance_data_source_transfer_payload(gi.payload);
+
+    require(governance_data_source_index() < claim_payload.governance_data_source_index, PythError::OldGovernanceMessage);
+
+    set_governance_data_source_index(claim_payload.governance_data_source_index);
+
+    let new_governance_data_source = DataSource {
+        chain_id: vm.emitter_chain_id,
+        emitter_address: vm.emitter_address,
+    };
+
+    set_governance_data_source(new_governance_data_source);
+
+    // Setting the last executed governance to the claimVaa sequence to avoid using older sequences.
+    set_last_executed_governance_sequence(vm.sequence);
+
+    log(GovernanceDataSourceSetEvent {
+        old_data_source: old_governance_data_source,
+        new_data_source: new_governance_data_source,
+        initial_sequence: vm.sequence,
+    });
+}
+
+#[storage(read, write)]
+fn set_data_sources(payload: SetDataSourcesPayload) {
+    let old_data_sources = storage.valid_data_sources.load_vec();
+
+    let mut i = 0;
+    while i < old_data_sources.len {
+        let data_source = old_data_sources.get(i).unwrap();
+        storage.is_valid_data_source.insert(data_source, false);
+        i += 1;
+    }
+
+    // Clear the current list of valid data sources
+    storage.valid_data_sources.clear();
+
+    i = 0;
+    // Add new data sources from the payload and mark them as valid
+    while i < payload.data_sources.len {
+        let data_source = payload.data_sources.get(i).unwrap();
+        storage.valid_data_sources.push(data_source);
+        storage.is_valid_data_source.insert(data_source, true);
+
+        i += 1;
+    }
+
+    // Emit an event with the old and new data sources
+    log(DataSourcesSetEvent {
+        old_data_sources: old_data_sources,
+        new_data_sources: storage.valid_data_sources.load_vec(),
+    });
+}
+
+#[storage(read, write)]
+fn set_fee(payload: SetFeePayload) {
+    let old_fee = storage.single_update_fee.read();
+    storage.single_update_fee.write(payload.new_fee);
+
+    log(FeeSetEvent {
+        old_fee,
+        new_fee: payload.new_fee,
+    });
+}
+
+#[storage(read, write)]
+fn set_valid_period(payload: SetValidPeriodPayload) {
+    let old_valid_period = storage.valid_time_period_seconds.read();
+    storage.valid_time_period_seconds.write(payload.new_valid_period);
+
+    log(ValidPeriodSetEvent {
+        old_valid_period,
+        new_valid_period: payload.new_valid_period,
+    });
+}
+
+abi PythGovernance {
+    #[storage(read)]
+    fn governance_data_source() -> DataSource;
+
+    #[storage(read, write)]
+    fn execute_governance_instruction(encoded_vm: Bytes);
+}
+
+impl PythGovernance for Contract {
+    #[storage(read)]
+    fn governance_data_source() -> DataSource {
+        governance_data_source()
+    }
+
+    #[storage(read, write)]
+    fn execute_governance_instruction(encoded_vm: Bytes) {
+        let vm = verify_governance_vm(encoded_vm);
+        // Log so that the WormholeVM struct will show up in the ABI and can be used in the tests
+        log(vm);
+
+        let gi = GovernanceInstruction::parse_governance_instruction(vm.payload);
+        // Log so that the GovernanceInstruction struct will show up in the ABI and can be used in the tests
+        log(gi);
+
+        require(gi.target_chain_id == chain_id() || gi.target_chain_id == 0, PythError::InvalidGovernanceTarget);
+
+        match gi.action {
+            GovernanceAction::UpgradeContract => {
+                require(gi.target_chain_id != 0, PythError::InvalidGovernanceTarget);
+                // TODO: implement upgrade_upgradeable_contract(uc) when Fuel releases the upgrade standard library;
+                log("Upgrade functionality not implemented");
+                revert(0u64);
+            },
+            GovernanceAction::AuthorizeGovernanceDataSourceTransfer => {
+                let agdst = GovernanceInstruction::parse_authorize_governance_data_source_transfer_payload(gi.payload);
+                log(agdst);
+                authorize_governance_data_source_transfer(agdst);
+            },
+            GovernanceAction::SetDataSources => {
+                let sdsp = GovernanceInstruction::parse_set_data_sources_payload(gi.payload);
+                log(sdsp);
+                set_data_sources(sdsp);
+            },
+            GovernanceAction::SetFee => {
+                let sf = GovernanceInstruction::parse_set_fee_payload(gi.payload);
+                log(sf);
+                set_fee(sf);
+            },
+            GovernanceAction::SetValidPeriod => {
+                let svp = GovernanceInstruction::parse_set_valid_period_payload(gi.payload);
+                log(svp);
+                set_valid_period(svp);
+            },
+            GovernanceAction::RequestGovernanceDataSourceTransfer => {
+                // RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message
+                // The `revert` function only accepts u64, so as
+                // a workaround we use require.
+                require(false, PythError::InvalidGovernanceMessage);
+            },
+            _ => {
+                // The `revert` function only accepts u64, so as
+                // a workaround we use require.
+                require(false, PythError::InvalidGovernanceMessage);
+            }
+        }
+    }
+}
+
+
+#[storage(read, write)]
+fn verify_governance_vm(encoded_vm: Bytes) -> WormholeVM {
+    let vm = WormholeVM::parse_and_verify_wormhole_vm(
+        current_guardian_set_index(),
+        encoded_vm,
+        storage
+            .wormhole_guardian_sets,
+    );
+
+    require(
+        storage
+            .governance_data_source
+            .read()
+            .is_valid_governance_data_source(vm.emitter_chain_id, vm.emitter_address),
+        PythError::InvalidGovernanceDataSource,
+    );
+
+    require(
+        vm.sequence > last_executed_governance_sequence(),
+        PythError::OldGovernanceMessage,
+    );
+
+    set_last_executed_governance_sequence(vm.sequence);
+    vm
+}

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

@@ -2,4 +2,6 @@ library;
 
 pub mod data_source;
 pub mod price;
+pub mod governance_payload;
+pub mod governance_instruction;
 pub mod wormhole_light;

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

@@ -0,0 +1,29 @@
+library;
+
+use std::bytes::Bytes;
+
+pub struct GovernanceInstruction {
+    magic: u32,
+    module: GovernanceModule,
+    action: GovernanceAction,
+    target_chain_id: u16,
+    payload: Bytes,
+}
+
+pub enum GovernanceModule {
+    Executor: (), // 0
+    Target: (), // 1
+    EvmExecutor: (), // 2
+    StacksTarget: (), // 3
+    Invalid: (),
+}
+
+pub enum GovernanceAction {
+    UpgradeContract: (), // 0
+    AuthorizeGovernanceDataSourceTransfer: (), // 1
+    SetDataSources: (), // 2
+    SetFee: (), // 3
+    SetValidPeriod: (), // 4
+    RequestGovernanceDataSourceTransfer: (), // 5
+    Invalid: (),
+}

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

@@ -0,0 +1,29 @@
+library;
+
+use std::bytes::Bytes;
+
+use ::data_structures::data_source::DataSource;
+
+pub struct UpgradeContractPayload {
+    new_implementation: Identity,
+}
+
+pub struct AuthorizeGovernanceDataSourceTransferPayload {
+    claim_vaa: Bytes,
+}
+
+pub struct RequestGovernanceDataSourceTransferPayload {
+    governance_data_source_index: u32,
+}
+
+pub struct SetDataSourcesPayload {
+    data_sources: Vec<DataSource>,
+}
+
+pub struct SetFeePayload {
+    new_fee: u64,
+}
+
+pub struct SetValidPeriodPayload {
+    new_valid_period: u64,
+}

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

@@ -4,8 +4,3 @@ pub struct GuardianSet {
     expiration_time: u64,
     keys: Vec<b256>,
 }
-
-pub struct WormholeProvider {
-    governance_chain_id: u16,
-    governance_contract: b256,
-}

+ 8 - 4
target_chains/fuel/contracts/pyth-interface/src/interface.sw

@@ -9,9 +9,9 @@ use ::data_structures::{
         PriceFeed,
         PriceFeedId,
     },
+    governance_payload::UpgradeContractPayload,
     wormhole_light::{
         GuardianSet,
-        WormholeProvider,
     },
 };
 use std::{bytes::Bytes, storage::storage_vec::*};
@@ -259,9 +259,13 @@ abi PythInit {
     #[storage(read, write)]
     fn constructor(
         data_sources: Vec<DataSource>,
+        governance_data_source: DataSource,
+        wormhole_governance_data_source: DataSource,
         single_update_fee: u64,
         valid_time_period_seconds: u64,
-        wormhole_guardian_set_upgrade: Bytes,
+        wormhole_guardian_set_addresses: Vec<b256>,
+        wormhole_guardian_set_index: u32,
+        chain_id: u16,
     );
 }
 
@@ -284,7 +288,7 @@ abi PythInfo {
     fn single_update_fee() -> u64;
 
     #[storage(read)]
-    fn valid_data_source(data_source: DataSource) -> bool;
+    fn is_valid_data_source(data_source: DataSource) -> bool;
 
     #[storage(read)]
     fn valid_data_sources() -> Vec<DataSource>;
@@ -295,7 +299,7 @@ abi WormholeGuardians {
     fn current_guardian_set_index() -> u32;
 
     #[storage(read)]
-    fn current_wormhole_provider() -> WormholeProvider;
+    fn current_wormhole_provider() -> DataSource;
 
     #[storage(read)]
     fn governance_action_is_consumed(hash: b256) -> bool;

+ 27 - 4
target_chains/fuel/contracts/scripts/deploy_pyth.rs

@@ -2,13 +2,13 @@ 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::BETA_5_URL, pyth_utils::guardian_set_upgrade_4_addresses};
 use pyth_sdk::{
     constants::{
-        BTC_USD_PRICE_FEED_ID, DEFAULT_VALID_TIME_PERIOD, ETH_USD_PRICE_FEED_ID,
+        BTC_USD_PRICE_FEED_ID, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID, ETH_USD_PRICE_FEED_ID,
         USDC_USD_PRICE_FEED_ID,
     },
-    pyth_utils::{update_data_bytes, Pyth},
+    pyth_utils::{update_data_bytes, DataSource, Pyth},
 };
 
 #[tokio::main]
@@ -26,8 +26,31 @@ async fn main() {
 
     let pyth = Pyth::deploy(admin).await.unwrap();
 
+    let governance_data_source: DataSource = DataSource {
+        chain_id: 1,
+        emitter_address: Bits256::from_hex_str(
+            "5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e",
+        )
+        .unwrap(),
+    };
+
+    let wormhole_governance_data_source: DataSource = DataSource {
+        chain_id: 1,
+        emitter_address: 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,
+        ]),
+    };
+
     let _ = pyth
-        .constructor(DEFAULT_VALID_TIME_PERIOD, guardian_set_upgrade_4_vaa())
+        .constructor(
+            governance_data_source,
+            wormhole_governance_data_source,
+            DEFAULT_VALID_TIME_PERIOD,
+            guardian_set_upgrade_4_addresses(),
+            4,
+            DUMMY_CHAIN_ID,
+        )
         .await
         .unwrap();
 

+ 22 - 1
target_chains/fuel/contracts/src/constants.rs

@@ -1,6 +1,27 @@
-use crate::pyth_utils::{Price, PriceFeed};
+use crate::pyth_utils::{DataSource, Price, PriceFeed};
 use fuels::types::Bits256;
 
+pub const MAGIC: u32 = 0x5054474d;
+
+pub const GOVERNANCE_DATA_SOURCE: DataSource = DataSource {
+    chain_id: 1,
+    emitter_address: Bits256([
+        0x56, 0x35, 0x97, 0x9a, 0x22, 0x1c, 0x34, 0x93, 0x1e, 0x32, 0x62, 0x0b, 0x92, 0x93, 0xa4, 0x63,
+        0x06, 0x55, 0x55, 0xea, 0x71, 0xfe, 0x97, 0xcd, 0x62, 0x37, 0xad, 0xe8, 0x75, 0xb1, 0x2e, 0x9e
+    ]),
+};
+
+// only used for updating guardian set
+pub const WORMHOLE_GOVERNANCE_DATA_SOURCE: DataSource = DataSource {
+    chain_id: 1,
+    emitter_address: 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,
+    ]),
+};
+
+pub const DUMMY_CHAIN_ID: u16 = 1;
+
 pub const BETA_5_URL: &str = "beta-5.fuel.network";
 pub const BETA_5_PYTH_CONTRACT_ID: &str =
     "0xe69daeb9fcf4c536c0fe402403b4b9e9822cc8b1f296e5d754be12cc384554c5";

+ 148 - 2
target_chains/fuel/contracts/src/pyth_utils.rs

@@ -16,7 +16,9 @@ use fuels::{
 use rand::Rng;
 use reqwest;
 use serde_json;
+use serde_wormhole::RawMessage;
 use std::path::PathBuf;
+use wormhole_sdk::Vaa;
 
 abigen!(Contract(
     name = "PythOracleContract",
@@ -87,6 +89,80 @@ pub fn test_accumulator_update_data_bytes() -> Vec<Bytes> {
     )]
 }
 
+pub fn create_set_fee_payload(new_fee: u64, exponent: u64) -> Vec<u8> {
+    let base = new_fee / 10u64.pow(exponent.try_into().unwrap());
+    let base_bytes = base.to_be_bytes();
+    let exponent_bytes = exponent.to_be_bytes();
+    let mut payload = Vec::new();
+    payload.extend_from_slice(&base_bytes);
+    payload.extend_from_slice(&exponent_bytes);
+    payload
+}
+
+pub fn create_set_valid_period_payload(new_valid_period: u64) -> Vec<u8> {
+    let valid_period_bytes = new_valid_period.to_be_bytes();
+    let mut payload = Vec::new();
+    payload.extend_from_slice(&valid_period_bytes);
+    payload
+}
+
+pub fn create_set_data_sources_payload(data_sources: Vec<DataSource>) -> Vec<u8> {
+    let mut payload = Vec::new();
+    payload.push(data_sources.len() as u8);
+    for data_source in data_sources {
+        payload.extend_from_slice(&data_source.chain_id.to_be_bytes());
+        payload.extend_from_slice(&data_source.emitter_address.0);
+    }
+    payload
+}
+
+pub fn create_authorize_governance_data_source_transfer_payload(
+    claim_vaa: Vaa<Box<RawMessage>>,
+) -> Vec<u8> {
+    serde_wormhole::to_vec(&claim_vaa).unwrap()
+}
+
+pub fn create_request_governance_data_source_transfer_payload(
+    governance_data_source_index: u32,
+) -> Vec<u8> {
+    let index_bytes = governance_data_source_index.to_be_bytes();
+    let mut payload = Vec::new();
+    payload.extend_from_slice(&index_bytes);
+    payload
+}
+
+pub fn create_governance_instruction_payload(
+    magic: u32,
+    module: GovernanceModule,
+    action: GovernanceAction,
+    target_chain_id: u16,
+    payload: Vec<u8>,
+) -> Vec<u8> {
+    let mut buffer = Vec::new();
+    buffer.extend_from_slice(&magic.to_be_bytes());
+    let module_number = match module {
+        GovernanceModule::Executor => 0,
+        GovernanceModule::Target => 1,
+        GovernanceModule::EvmExecutor => 2,
+        GovernanceModule::StacksTarget => 3,
+        GovernanceModule::Invalid => u8::MAX, // Typically 255 for invalid
+    };
+    buffer.push(module_number);
+    let action_number = match action {
+        GovernanceAction::UpgradeContract => 0,
+        GovernanceAction::AuthorizeGovernanceDataSourceTransfer => 1,
+        GovernanceAction::SetDataSources => 2,
+        GovernanceAction::SetFee => 3,
+        GovernanceAction::SetValidPeriod => 4,
+        GovernanceAction::RequestGovernanceDataSourceTransfer => 5,
+        GovernanceAction::Invalid => u8::MAX, // Typically 255 for invalid
+    };
+    buffer.push(action_number);
+    buffer.extend_from_slice(&target_chain_id.to_be_bytes());
+    buffer.extend_from_slice(&payload);
+    buffer
+}
+
 impl Pyth {
     pub async fn price(&self, price_feed_id: Bits256) -> Result<FuelCallResponse<Price>, Error> {
         self.instance
@@ -119,16 +195,24 @@ impl Pyth {
 
     pub async fn constructor(
         &self,
+        governance_data_source: DataSource,
+        wormhole_governance_data_source: DataSource,
         valid_time_period_seconds: u64,
-        wormhole_guardian_set_upgrade: Bytes,
+        wormhole_guardian_set_addresses: Vec<Bits256>,
+        wormhole_guardian_set_index: u32,
+        chain_id: u16,
     ) -> Result<FuelCallResponse<()>, Error> {
         self.instance
             .methods()
             .constructor(
                 default_data_sources(),
+                governance_data_source,
+                wormhole_governance_data_source,
                 DEFAULT_SINGLE_UPDATE_FEE,
                 valid_time_period_seconds,
-                wormhole_guardian_set_upgrade,
+                wormhole_guardian_set_addresses,
+                wormhole_guardian_set_index,
+                chain_id,
             )
             .with_tx_policies(TxPolicies::default().with_gas_price(1))
             .call()
@@ -173,6 +257,68 @@ pub fn guardian_set_upgrade_4_vaa() -> Bytes {
     Bytes(hex::decode(GUARDIAN_SET_UPGRADE_4_VAA).unwrap())
 }
 
+// Full list of guardian set upgrade 3 addresses can be found here: https://github.com/wormhole-foundation/wormhole-networks/blob/master/mainnetv2/guardianset/v3.prototxt
+pub fn guardian_set_upgrade_3_addresses() -> Vec<Bits256> {
+    let addresses = vec![
+        "58CC3AE5C097b213cE3c81979e1B9f9570746AA5", // Certus One
+        "fF6CB952589BDE862c25Ef4392132fb9D4A42157", // Staked
+        "114De8460193bdf3A2fCf81f86a09765F4762fD1", // Figment
+        "107A0086b32d7A0977926A205131d8731D39cbEB", // ChainodeTech
+        "8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2", // Inotel
+        "11b39756C042441BE6D8650b69b54EbE715E2343", // HashQuark
+        "54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd", // Chainlayer
+        "15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20", // xLabs
+        "74a3bf913953D695260D88BC1aA25A4eeE363ef0", // Forbole
+        "000aC0076727b35FBea2dAc28fEE5cCB0fEA768e", // Staking Fund
+        "AF45Ced136b9D9e24903464AE889F5C8a723FC14", // Moonlet Wallet
+        "f93124b7c738843CBB89E864c862c38cddCccF95", // P2P
+        "D2CC37A4dc036a8D232b48f62cDD4731412f4890", // 01Node
+        "DA798F6896A3331F64b48c12D1D57Fd9cbe70811", // MCF
+        "71AA1BE1D36CaFE3867910F99C09e347899C19C3", // Everstake
+        "8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf", // Chorus One
+        "178e21ad2E77AE06711549CFBB1f9c7a9d8096e8", // Syncnode
+        "5E1487F35515d02A92753504a8D75471b9f49EdB", // Triton
+        "6FbEBc898F403E4773E95feB15E80C9A99c8348d", // Staking Facilities
+    ];
+
+    // Convert the addresses to Bits256 by padding the leftmost 12 bytes with zeros. This is done because the original 20-byte key is shorter than the 32-byte b256 type.
+    addresses
+        .iter()
+        .map(|&addr| Bits256::from_hex_str(&format!("{:0>64}", addr)).unwrap())
+        .collect()
+}
+
+// Full list of guardian set upgrade 4 addresses can be found here: https://github.com/wormhole-foundation/wormhole-networks/blob/master/mainnetv2/guardianset/v4.prototxt
+pub fn guardian_set_upgrade_4_addresses() -> Vec<Bits256> {
+    let addresses = vec![
+        "5893B5A76c3f739645648885bDCcC06cd70a3Cd3", // RockawayX
+        "fF6CB952589BDE862c25Ef4392132fb9D4A42157", // Staked
+        "114De8460193bdf3A2fCf81f86a09765F4762fD1", // Figment
+        "107A0086b32d7A0977926A205131d8731D39cbEB", // ChainodeTech
+        "8C82B2fd82FaeD2711d59AF0F2499D16e726f6b2", // Inotel
+        "11b39756C042441BE6D8650b69b54EbE715E2343", // HashQuark
+        "54Ce5B4D348fb74B958e8966e2ec3dBd4958a7cd", // Chainlayer
+        "15e7cAF07C4e3DC8e7C469f92C8Cd88FB8005a20", // xLabs
+        "74a3bf913953D695260D88BC1aA25A4eeE363ef0", // Forbole
+        "000aC0076727b35FBea2dAc28fEE5cCB0fEA768e", // Staking Fund
+        "AF45Ced136b9D9e24903464AE889F5C8a723FC14", // Moonlet Wallet
+        "f93124b7c738843CBB89E864c862c38cddCccF95", // P2P
+        "D2CC37A4dc036a8D232b48f62cDD4731412f4890", // 01Node
+        "DA798F6896A3331F64b48c12D1D57Fd9cbe70811", // MCF
+        "71AA1BE1D36CaFE3867910F99C09e347899C19C3", // Everstake
+        "8192b6E7387CCd768277c17DAb1b7a5027c0b3Cf", // Chorus One
+        "178e21ad2E77AE06711549CFBB1f9c7a9d8096e8", // Syncnode
+        "5E1487F35515d02A92753504a8D75471b9f49EdB", // Triton
+        "6FbEBc898F403E4773E95feB15E80C9A99c8348d", // Staking Facilities
+    ];
+
+    // Convert the addresses to Bits256 by padding the leftmost 12 bytes with zeros. This is done because the original 20-byte key is shorter than the 32-byte b256 type.
+    addresses
+        .iter()
+        .map(|&addr| Bits256::from_hex_str(&format!("{:0>64}", addr)).unwrap())
+        .collect()
+}
+
 pub fn default_price_feed_ids() -> Vec<Bits256> {
     vec![
         Bits256(

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

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

+ 15 - 6
target_chains/fuel/contracts/tests/functions/pyth_core/ema_price.rs

@@ -5,12 +5,13 @@ use crate::utils::interface::{
 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,
+        DEFAULT_SINGLE_UPDATE_FEE, DUMMY_CHAIN_ID, GOVERNANCE_DATA_SOURCE,
+        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,
+        WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -26,9 +27,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             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(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -64,9 +69,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             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(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 14 - 5
target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_no_older_than.rs

@@ -4,12 +4,13 @@ use crate::utils::interface::{
 };
 use pyth_sdk::{
     constants::{
-        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, 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,
+        TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -26,9 +27,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -72,9 +77,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 15 - 6
target_chains/fuel/contracts/tests/functions/pyth_core/ema_price_unsafe.rs

@@ -5,12 +5,13 @@ use crate::utils::interface::{
 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,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, GOVERNANCE_DATA_SOURCE,
+        TEST_ACCUMULATOR_ETH_USD_PRICE_FEED, TEST_ACCUMULATOR_USDC_USD_PRICE_FEED,
+        TEST_BATCH_ETH_USD_PRICE_FEED, TEST_BATCH_USDC_USD_PRICE_FEED,
+        WORMHOLE_GOVERNANCE_DATA_SOURCE, DUMMY_CHAIN_ID
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -26,9 +27,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -64,9 +69,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 14 - 5
target_chains/fuel/contracts/tests/functions/pyth_core/parse_price_feed_updates.rs

@@ -5,12 +5,13 @@ use crate::utils::interface::{
 use crate::utils::setup::setup_environment;
 use pyth_sdk::{
     constants::{
-        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, 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_BATCH_USDC_USD_PRICE_FEED, WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -26,9 +27,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -59,9 +64,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 15 - 7
target_chains/fuel/contracts/tests/functions/pyth_core/price.rs

@@ -2,16 +2,16 @@ 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,
+        DEFAULT_SINGLE_UPDATE_FEE, DUMMY_CHAIN_ID, GOVERNANCE_DATA_SOURCE,
+        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,
+        WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -26,9 +26,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             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(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -64,9 +68,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             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(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 14 - 5
target_chains/fuel/contracts/tests/functions/pyth_core/price_no_older_than.rs

@@ -5,12 +5,13 @@ use crate::utils::interface::{
 use crate::utils::setup::setup_environment;
 use pyth_sdk::{
     constants::{
-        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, 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,
+        TEST_BATCH_USDC_USD_PRICE_FEED, TEST_EXTENDED_TIME_PERIOD, WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -27,9 +28,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -73,9 +78,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 14 - 5
target_chains/fuel/contracts/tests/functions/pyth_core/price_unsafe.rs

@@ -5,12 +5,13 @@ use crate::utils::interface::{
 use crate::utils::setup::setup_environment;
 use pyth_sdk::{
     constants::{
-        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, 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_BATCH_USDC_USD_PRICE_FEED, WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -25,9 +26,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -63,9 +68,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 15 - 4
target_chains/fuel/contracts/tests/functions/pyth_core/update_fee.rs

@@ -1,9 +1,12 @@
 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},
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE,
+    },
     pyth_utils::{
-        default_data_sources, guardian_set_upgrade_3_vaa, test_accumulator_update_data_bytes,
+        default_data_sources, guardian_set_upgrade_3_addresses, test_accumulator_update_data_bytes,
         test_batch_update_data_bytes,
     },
 };
@@ -18,9 +21,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -38,9 +45,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 15 - 5
target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds.rs

@@ -3,12 +3,14 @@ use crate::utils::interface::{
     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},
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE,
+    },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -23,9 +25,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -69,9 +75,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 15 - 4
target_chains/fuel/contracts/tests/functions/pyth_core/update_price_feeds_if_necessary.rs

@@ -5,9 +5,12 @@ use crate::utils::interface::{
 };
 use crate::utils::setup::setup_environment;
 use pyth_sdk::{
-    constants::{DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD},
+    constants::{
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE,
+    },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -22,9 +25,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -75,9 +82,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 229 - 0
target_chains/fuel/contracts/tests/functions/pyth_governance/execute_governance_instruction.rs

@@ -0,0 +1,229 @@
+use {
+    crate::utils::{interface::pyth_init::constructor, setup::setup_environment},
+    fuels::types::{Bits256, Bytes},
+    pyth_sdk::{
+        constants::{
+            DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+            GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE,
+        },
+        pyth_utils::default_data_sources,
+    },
+    pythnet_sdk::test_utils::{create_vaa_from_payload, dummy_guardians_addresses},
+};
+
+mod success {
+
+    use {
+        super::*,
+        crate::utils::interface::{
+            pyth_core::valid_time_period,
+            pyth_governance::{execute_governance_instruction, governance_data_source},
+            pyth_info::{single_update_fee, valid_data_sources},
+        },
+        pyth_sdk::{
+            constants::MAGIC,
+            pyth_utils::{
+                create_authorize_governance_data_source_transfer_payload,
+                create_governance_instruction_payload,
+                create_request_governance_data_source_transfer_payload,
+                create_set_data_sources_payload, create_set_fee_payload,
+                create_set_valid_period_payload, DataSource, GovernanceAction, GovernanceModule,
+                Pyth,
+            },
+        },
+    };
+
+    async fn setup_governance_test_environment() -> (Pyth, Vec<Bits256>) {
+        let (_oracle_contract_id, deployer) = setup_environment().await.unwrap();
+        let dummy_guardians_addresses: Vec<[u8; 20]> = dummy_guardians_addresses();
+        let bits256_guardians: Vec<Bits256> = dummy_guardians_addresses
+            .iter()
+            .map(|address| {
+                let mut full_address = [0u8; 32]; // Create a 32-byte array filled with zeros
+                full_address[12..].copy_from_slice(address); // Copy the 20-byte address into the rightmost part
+                Bits256(full_address) // Create Bits256 from the 32-byte array
+            })
+            .collect();
+
+        constructor(
+            &deployer.instance,
+            default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
+            DEFAULT_SINGLE_UPDATE_FEE,
+            DEFAULT_VALID_TIME_PERIOD,
+            bits256_guardians.clone(),
+            0,
+            DUMMY_CHAIN_ID,
+        )
+        .await;
+
+        (deployer, bits256_guardians)
+    }
+
+    #[tokio::test]
+    async fn test_set_fee() {
+        let (deployer, _bits256_guardians) = setup_governance_test_environment().await;
+        // Test SetFee logic here
+        let set_fee_payload = create_set_fee_payload(100, 1);
+        let governance_instruction_payload = create_governance_instruction_payload(
+            MAGIC,
+            GovernanceModule::Target,
+            GovernanceAction::SetFee,
+            1,
+            set_fee_payload,
+        );
+
+        let vaa = create_vaa_from_payload(
+            &governance_instruction_payload,
+            wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0),
+            wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id),
+            1,
+        );
+
+        execute_governance_instruction(
+            &deployer.instance,
+            Bytes(serde_wormhole::to_vec(&vaa).unwrap()),
+        )
+        .await;
+
+        let fee = single_update_fee(&deployer.instance).await.value;
+
+        assert_eq!(fee, 100);
+    }
+
+    #[tokio::test]
+    async fn test_set_valid_period() {
+        let (deployer, _bits256_guardians) = setup_governance_test_environment().await;
+        // Test SetValidPeriod logic here
+        let set_valid_period_payload = create_set_valid_period_payload(100);
+        let governance_instruction_payload = create_governance_instruction_payload(
+            MAGIC,
+            GovernanceModule::Target,
+            GovernanceAction::SetValidPeriod,
+            1,
+            set_valid_period_payload,
+        );
+
+        let vaa = create_vaa_from_payload(
+            &governance_instruction_payload,
+            wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0),
+            wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id),
+            2,
+        );
+
+        execute_governance_instruction(
+            &deployer.instance,
+            Bytes(serde_wormhole::to_vec(&vaa).unwrap()),
+        )
+        .await;
+
+        let valid_period = valid_time_period(&deployer.instance).await.value;
+
+        assert_eq!(valid_period, 100);
+    }
+
+    #[tokio::test]
+    async fn test_set_data_sources() {
+        let (deployer, _bits256_guardians) = setup_governance_test_environment().await;
+        // Test SetDataSources
+        let test_data_sources = vec![
+            DataSource {
+                chain_id: 2,
+                emitter_address: Bits256([1u8; 32]),
+            },
+            DataSource {
+                chain_id: 27,
+                emitter_address: Bits256([2u8; 32]),
+            },
+        ];
+        let set_data_sources_payload = create_set_data_sources_payload(test_data_sources.clone());
+        let governance_instruction_payload = create_governance_instruction_payload(
+            MAGIC,
+            GovernanceModule::Target,
+            GovernanceAction::SetDataSources,
+            1,
+            set_data_sources_payload,
+        );
+        let vaa = create_vaa_from_payload(
+            &governance_instruction_payload,
+            wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0),
+            wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id),
+            3,
+        );
+
+        execute_governance_instruction(
+            &deployer.instance,
+            Bytes(serde_wormhole::to_vec(&vaa).unwrap()),
+        )
+        .await;
+
+        let new_data_sources = valid_data_sources(&deployer.instance).await.value;
+
+        assert_eq!(new_data_sources, test_data_sources);
+    }
+
+    #[tokio::test]
+    async fn test_authorize_governance_data_source_transfer() {
+        let (deployer, _bits256_guardians) = setup_governance_test_environment().await;
+        // Test AuthorizeGovernanceDataSourceTransfer
+        let new_emitter_address = Bits256([3u8; 32]);
+        let new_emitter_chain = 2;
+
+        // Simulate creating a RequestGovernanceDataSourceTransfer VAA
+        let request_governance_data_source_transfer_payload =
+            create_request_governance_data_source_transfer_payload(1);
+        let mut governance_instruction_payload = create_governance_instruction_payload(
+            MAGIC,
+            GovernanceModule::Target,
+            GovernanceAction::RequestGovernanceDataSourceTransfer,
+            1,
+            request_governance_data_source_transfer_payload,
+        );
+        let mut vaa = create_vaa_from_payload(
+            &governance_instruction_payload,
+            wormhole_sdk::Address(new_emitter_address.0),
+            wormhole_sdk::Chain::from(new_emitter_chain),
+            4,
+        );
+
+        // Authorize the transfer
+        let authorize_governance_data_source_transfer_payload =
+            create_authorize_governance_data_source_transfer_payload(vaa);
+        governance_instruction_payload = create_governance_instruction_payload(
+            MAGIC,
+            GovernanceModule::Target,
+            GovernanceAction::AuthorizeGovernanceDataSourceTransfer,
+            1,
+            authorize_governance_data_source_transfer_payload,
+        );
+        vaa = create_vaa_from_payload(
+            &governance_instruction_payload,
+            wormhole_sdk::Address(GOVERNANCE_DATA_SOURCE.emitter_address.0),
+            wormhole_sdk::Chain::from(GOVERNANCE_DATA_SOURCE.chain_id),
+            5,
+        );
+
+        let old_governance_data_source = governance_data_source(&deployer.instance).await;
+        execute_governance_instruction(
+            &deployer.instance,
+            Bytes(serde_wormhole::to_vec(&vaa).unwrap()),
+        )
+        .await;
+
+        let new_governance_data_source = governance_data_source(&deployer.instance).await;
+        assert_ne!(
+            old_governance_data_source.value.emitter_address,
+            new_governance_data_source.value.emitter_address
+        );
+        assert_ne!(
+            old_governance_data_source.value.chain_id,
+            new_governance_data_source.value.chain_id
+        );
+        assert_eq!(
+            new_governance_data_source.value.emitter_address,
+            new_emitter_address
+        );
+        assert_eq!(new_governance_data_source.value.chain_id, new_emitter_chain);
+    }
+}

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

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

+ 14 - 5
target_chains/fuel/contracts/tests/functions/pyth_info/price_feed_unsafe.rs

@@ -6,12 +6,13 @@ use crate::utils::interface::{
 use crate::utils::setup::setup_environment;
 use pyth_sdk::{
     constants::{
-        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, TEST_ACCUMULATOR_ETH_USD_PRICE_FEED,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, DUMMY_CHAIN_ID,
+        GOVERNANCE_DATA_SOURCE, 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_BATCH_USDC_USD_PRICE_FEED, WORMHOLE_GOVERNANCE_DATA_SOURCE,
     },
     pyth_utils::{
-        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_vaa,
+        default_data_sources, default_price_feed_ids, guardian_set_upgrade_3_addresses,
         test_accumulator_update_data_bytes, test_batch_update_data_bytes,
     },
 };
@@ -26,9 +27,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 
@@ -57,9 +62,13 @@ mod success {
         constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID,
         )
         .await;
 

+ 16 - 17
target_chains/fuel/contracts/tests/functions/pyth_init/constuctor.rs → target_chains/fuel/contracts/tests/functions/pyth_init/constructor.rs

@@ -1,6 +1,6 @@
 use crate::utils::interface::{
     pyth_core::valid_time_period,
-    pyth_info::{owner, single_update_fee, valid_data_source, valid_data_sources},
+    pyth_info::{is_valid_data_source, owner, single_update_fee, valid_data_sources},
     pyth_init::constructor,
     wormhole_guardians::{
         current_guardian_set_index, current_wormhole_provider, governance_action_is_consumed,
@@ -8,10 +8,10 @@ use crate::utils::interface::{
 };
 use pyth_sdk::{
     constants::{
-        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH,
+        DEFAULT_SINGLE_UPDATE_FEE, DEFAULT_VALID_TIME_PERIOD, UPGRADE_3_VAA_GOVERNANCE_ACTION_HASH, GOVERNANCE_DATA_SOURCE, WORMHOLE_GOVERNANCE_DATA_SOURCE, DUMMY_CHAIN_ID
     },
     pyth_utils::{
-        default_data_sources, guardian_set_upgrade_3_vaa, ConstructedEvent, State, WormholeProvider,
+        default_data_sources, guardian_set_upgrade_3_addresses, ConstructedEvent, DataSource, State,
     },
 };
 
@@ -29,7 +29,7 @@ mod success {
 
         // Initial values
         assert!(
-            !valid_data_source(&deployer.instance, &default_data_sources()[0])
+            !is_valid_data_source(&deployer.instance, &default_data_sources()[0])
                 .await
                 .value
         );
@@ -50,9 +50,9 @@ mod success {
         );
         assert_eq!(
             current_wormhole_provider(&deployer.instance,).await.value,
-            WormholeProvider {
-                governance_chain_id: 0,
-                governance_contract: Bits256::zeroed(),
+            DataSource {
+                chain_id: 0,
+                emitter_address: Bits256::zeroed(),
             }
         );
         assert_eq!(owner(&deployer.instance,).await.value, State::Uninitialized);
@@ -60,9 +60,13 @@ mod success {
         let response = constructor(
             &deployer.instance,
             default_data_sources(),
+            GOVERNANCE_DATA_SOURCE,
+            WORMHOLE_GOVERNANCE_DATA_SOURCE,
             DEFAULT_SINGLE_UPDATE_FEE,
             DEFAULT_VALID_TIME_PERIOD,
-            guardian_set_upgrade_3_vaa(),
+            guardian_set_upgrade_3_addresses(),
+            3,
+            DUMMY_CHAIN_ID
         )
         .await;
 
@@ -79,7 +83,7 @@ mod success {
 
         // Final values
         assert!(
-            valid_data_source(&deployer.instance, &default_data_sources()[0])
+            is_valid_data_source(&deployer.instance, &default_data_sources()[0])
                 .await
                 .value
         );
@@ -95,20 +99,15 @@ mod success {
             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([
+            DataSource {
+                chain_id: 1,
+                emitter_address: 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
                 ])

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

@@ -1 +1 @@
-pub(crate) mod constuctor;
+pub(crate) mod constructor;

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

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

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

@@ -0,0 +1,27 @@
+use fuels::{
+    accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, types::Bytes,
+};
+use pyth_sdk::pyth_utils::{DataSource, PythOracleContract};
+
+pub(crate) async fn execute_governance_instruction(
+    contract: &PythOracleContract<WalletUnlocked>,
+    encoded_vm: Bytes,
+) -> FuelCallResponse<()> {
+    contract
+        .methods()
+        .execute_governance_instruction(encoded_vm)
+        .call()
+        .await
+        .unwrap()
+}
+
+pub(crate) async fn governance_data_source(
+    contract: &PythOracleContract<WalletUnlocked>,
+) -> FuelCallResponse<DataSource> {
+    contract
+        .methods()
+        .governance_data_source()
+        .call()
+        .await
+        .unwrap()
+}

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

@@ -40,13 +40,13 @@ pub(crate) async fn single_update_fee(
     contract.methods().single_update_fee().call().await.unwrap()
 }
 
-pub(crate) async fn valid_data_source(
+pub(crate) async fn is_valid_data_source(
     contract: &PythOracleContract<WalletUnlocked>,
     data_source: &DataSource,
 ) -> FuelCallResponse<bool> {
     contract
         .methods()
-        .valid_data_source(data_source.clone())
+        .is_valid_data_source(data_source.clone())
         .call()
         .await
         .unwrap()

+ 12 - 3
target_chains/fuel/contracts/tests/utils/interface/pyth_init.rs

@@ -1,5 +1,6 @@
 use fuels::{
-    accounts::wallet::WalletUnlocked, prelude::Bytes, programs::call_response::FuelCallResponse,
+    accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse,
+    types::Bits256,
 };
 
 use pyth_sdk::pyth_utils::{DataSource, PythOracleContract};
@@ -7,17 +8,25 @@ use pyth_sdk::pyth_utils::{DataSource, PythOracleContract};
 pub(crate) async fn constructor(
     contract: &PythOracleContract<WalletUnlocked>,
     data_sources: Vec<DataSource>,
+    governance_data_source: DataSource,
+    wormhole_governance_data_source: DataSource,
     single_update_fee: u64,
     valid_time_period_seconds: u64,
-    wormhole_guardian_set_upgrade: Bytes,
+    wormhole_guardian_set_addresses: Vec<Bits256>,
+    wormhole_guardian_set_index: u32,
+    chain_id: u16,
 ) -> FuelCallResponse<()> {
     contract
         .methods()
         .constructor(
             data_sources,
+            governance_data_source,
+            wormhole_governance_data_source,
             single_update_fee,
             valid_time_period_seconds,
-            wormhole_guardian_set_upgrade,
+            wormhole_guardian_set_addresses,
+            wormhole_guardian_set_index,
+            chain_id,
         )
         .call()
         .await

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

@@ -1,7 +1,7 @@
 use fuels::{
     accounts::wallet::WalletUnlocked, programs::call_response::FuelCallResponse, types::Bits256,
 };
-use pyth_sdk::pyth_utils::{GuardianSet, PythOracleContract, WormholeProvider};
+use pyth_sdk::pyth_utils::{GuardianSet, PythOracleContract, DataSource};
 
 pub(crate) async fn current_guardian_set_index(
     contract: &PythOracleContract<WalletUnlocked>,
@@ -16,7 +16,7 @@ pub(crate) async fn current_guardian_set_index(
 
 pub(crate) async fn current_wormhole_provider(
     contract: &PythOracleContract<WalletUnlocked>,
-) -> FuelCallResponse<WormholeProvider> {
+) -> FuelCallResponse<DataSource> {
     contract
         .methods()
         .current_wormhole_provider()