feat(near): accumulators

This commit is contained in:
Reisen 2023-10-31 07:46:36 +00:00 committed by Reisen
parent 16832aba51
commit 9d8c271607
17 changed files with 4412 additions and 160 deletions

3499
target_chains/near/example/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
[package]
name = "pyth-near-example"
version = "0.1.0"
authors = ["Pyth Data Association"]
edition = "2021"
description = "A Pyth Receiver for Near"
[lib]
name = "pyth_example"
crate-type = ["cdylib", "lib"]
[dependencies]
byteorder = { version = "1.4.3" }
hex = { version = "0.4.3" }
near-sdk = { version = "4.1.1" }
num-traits = { version = "0.2.15" }
num-derive = { version = "0.3.3" }
pythnet-sdk = { path = "../../../pythnet/pythnet_sdk" }
pyth-near = { path = "../receiver", features = ["library"] }
strum = { version = "0.24.1", features = ["derive"] }
thiserror = { version = "1.0.38" }
wormhole-core = { git = "https://github.com/wormhole-foundation/wormhole", rev = "4ddeca4dbdba50e2cbf6e603242f8c75d9246e2a" }
[patch.crates-io]
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", rev = "4ddeca4dbdba50e2cbf6e603242f8c75d9246e2a" }
[dev-dependencies]
lazy_static = { version = "1.4.0" }
serde_json = { version = "1.0.91" }
serde = { version = "1.0.152", features = ["derive"] }
tokio = { version = "1.23.0", features = ["full"] }
workspaces = { version = "0.7.0" }
[profile.release]
codegen-units = 1
opt-level = 3
lto = "fat"
debug = false
panic = "abort"
overflow-checks = true

View File

@ -0,0 +1 @@
1.69.0-x86_64-unknown-linux-gnu

View File

@ -0,0 +1,65 @@
use {
near_sdk::{
borsh::{
self,
BorshDeserialize,
BorshSerialize,
},
env,
is_promise_success,
near_bindgen,
AccountId,
Gas,
PanicOnDefault,
Promise,
PromiseError,
},
pyth::state::PriceIdentifier,
};
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
pub struct PythExample {
pyth: AccountId,
}
#[near_bindgen]
impl PythExample {
#[init]
#[allow(clippy::new_without_default)]
pub fn new(pyth: AccountId) -> Self {
Self { pyth }
}
/// Get a Pyth Price Feed Result.
#[payable]
pub fn example_price_usage(&mut self, identifier: PriceIdentifier, data: String) -> Promise {
pyth::ext::ext_pyth::ext(self.pyth.clone())
.with_static_gas(Gas(30_000_000_000_000))
.with_attached_deposit(env::attached_deposit())
.update_price_feeds(data)
.then(
pyth::ext::ext_pyth::ext(self.pyth.clone())
.get_price(identifier)
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(10_000_000_000))
.handle_example_price_usage(),
),
)
}
#[payable]
#[private]
#[handle_result]
pub fn handle_example_price_usage(
&mut self,
#[callback_result] _r: Result<Option<pyth::state::Price>, PromiseError>,
) {
if !is_promise_success() {
return;
}
// Do things with Price Feed Result.
}
}

View File

@ -140,7 +140,7 @@ checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -199,6 +199,15 @@ dependencies = [
"zip",
]
[[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"
@ -291,7 +300,7 @@ dependencies = [
"borsh-schema-derive-internal",
"proc-macro-crate 0.1.5",
"proc-macro2",
"syn",
"syn 1.0.107",
]
[[package]]
@ -302,7 +311,7 @@ checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -313,7 +322,7 @@ checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -346,6 +355,26 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -605,7 +634,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn",
"syn 1.0.107",
]
[[package]]
@ -622,7 +651,7 @@ checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -635,7 +664,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn",
"syn 1.0.107",
]
[[package]]
@ -756,10 +785,19 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"synstructure",
]
[[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 = "1.8.0"
@ -1125,6 +1163,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 = "impl-codec"
version = "0.5.1"
@ -1142,7 +1186,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1364,7 +1408,7 @@ dependencies = [
"derive_more",
"near-crypto 0.15.0",
"near-primitives 0.15.0",
"num-rational",
"num-rational 0.3.2",
"serde",
"serde_json",
"sha2 0.10.6",
@ -1475,7 +1519,7 @@ dependencies = [
"near-primitives-core 0.14.0",
"near-rpc-error-macro 0.14.0",
"near-vm-errors 0.14.0",
"num-rational",
"num-rational 0.3.2",
"once_cell",
"primitive-types",
"rand 0.7.3",
@ -1505,7 +1549,7 @@ dependencies = [
"near-primitives-core 0.15.0",
"near-rpc-error-macro 0.15.0",
"near-vm-errors 0.15.0",
"num-rational",
"num-rational 0.3.2",
"once_cell",
"primitive-types",
"rand 0.7.3",
@ -1528,7 +1572,7 @@ dependencies = [
"bs58",
"derive_more",
"near-account-id 0.14.0",
"num-rational",
"num-rational 0.3.2",
"serde",
"sha2 0.10.6",
"strum",
@ -1545,7 +1589,7 @@ dependencies = [
"bs58",
"derive_more",
"near-account-id 0.15.0",
"num-rational",
"num-rational 0.3.2",
"serde",
"serde_repr",
"sha2 0.10.6",
@ -1560,7 +1604,7 @@ checksum = "93ee0b41c75ef859c193a8ff1dadfa0c8207bc0ac447cc22259721ad769a1408"
dependencies = [
"quote",
"serde",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1571,7 +1615,7 @@ checksum = "36addf90cc04bd547a627b3a292f59d7de4dd6fb5042115419ae901b93ce6c2d"
dependencies = [
"quote",
"serde",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1582,7 +1626,7 @@ checksum = "8e837bd4bacd807073ec5ceb85708da7f721b46a4c2a978de86027fb0034ce31"
dependencies = [
"near-rpc-error-core 0.14.0",
"serde",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1593,7 +1637,7 @@ checksum = "0b5beb352f3b91d8c491646c2fa4fdbbbf463c7b9c0226951c28f0197de44f99"
dependencies = [
"near-rpc-error-core 0.15.0",
"serde",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1643,7 +1687,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1709,6 +1753,20 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint 0.4.3",
"num-complex",
"num-integer",
"num-iter",
"num-rational 0.4.1",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.3.3"
@ -1720,6 +1778,26 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.3.3"
@ -1728,7 +1806,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1741,6 +1819,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.3.2"
@ -1748,12 +1837,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-bigint",
"num-bigint 0.3.3",
"num-integer",
"num-traits",
"serde",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint 0.4.3",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
@ -1817,7 +1918,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1862,7 +1963,7 @@ dependencies = [
"proc-macro-crate 1.2.1",
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1929,7 +2030,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2012,9 +2113,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.49"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
@ -2032,9 +2133,10 @@ dependencies = [
"num-traits",
"pyth-sdk 0.7.0",
"pyth-wormhole-attester-sdk",
"pythnet-sdk",
"serde",
"serde_json",
"serde_wormhole",
"serde_wormhole 0.1.0 (git+https://github.com/wormhole-foundation/wormhole)",
"strum",
"thiserror",
"tokio",
@ -2070,18 +2172,35 @@ dependencies = [
[[package]]
name = "pyth-wormhole-attester-sdk"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"hex 0.4.3",
"pyth-sdk 0.5.0",
"serde",
]
[[package]]
name = "pythnet-sdk"
version = "2.0.0"
dependencies = [
"bincode",
"borsh",
"bytemuck",
"byteorder",
"fast-math",
"hex 0.4.3",
"rustc_version",
"serde",
"sha3",
"slow_primes",
"thiserror",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@ -2346,7 +2465,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2426,7 +2545,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2437,7 +2556,7 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2459,7 +2578,7 @@ checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2474,6 +2593,16 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_wormhole"
version = "0.1.0"
source = "git+https://github.com/wormhole-foundation/wormhole?rev=4ddeca4dbdba50e2cbf6e603242f8c75d9246e2a#4ddeca4dbdba50e2cbf6e603242f8c75d9246e2a"
dependencies = [
"itoa",
"serde",
"thiserror",
]
[[package]]
name = "serde_wormhole"
version = "0.1.0"
@ -2558,6 +2687,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.10.0"
@ -2572,7 +2710,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2616,7 +2754,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2636,6 +2774,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.12.6"
@ -2644,7 +2793,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"unicode-xid",
]
@ -2690,22 +2839,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.38"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.38",
]
[[package]]
@ -2762,7 +2911,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2835,7 +2984,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -2992,7 +3141,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"wasm-bindgen-shared",
]
@ -3026,7 +3175,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -3250,7 +3399,7 @@ dependencies = [
"bstr",
"schemars",
"serde",
"serde_wormhole",
"serde_wormhole 0.1.0 (git+https://github.com/wormhole-foundation/wormhole?rev=4ddeca4dbdba50e2cbf6e603242f8c75d9246e2a)",
"sha3",
"thiserror",
]
@ -3287,7 +3436,7 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"synstructure",
]

View File

@ -9,6 +9,9 @@ description = "A Pyth Receiver for Near"
name = "pyth"
crate-type = ["cdylib", "lib"]
[features]
library = []
[dependencies]
byteorder = { version = "1.4.3" }
hex = { version = "0.4.3" }
@ -18,13 +21,14 @@ num-traits = { version = "0.2.15" }
num-derive = { version = "0.3.3" }
pyth-wormhole-attester-sdk = { path = "../../../wormhole_attester/sdk/rust" }
pyth-sdk = { version = "0.7.0" }
pythnet-sdk = { path = "../../../pythnet/pythnet_sdk" }
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole" }
strum = { version = "0.24.1", features = ["derive"] }
thiserror = { version = "1.0.38" }
wormhole-core = { git = "https://github.com/wormhole-foundation/wormhole" }
[patch.crates-io]
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole" }
serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", rev = "4ddeca4dbdba50e2cbf6e603242f8c75d9246e2a" }
[dev-dependencies]
lazy_static = { version = "1.4.0" }

View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
INIT_ARGS=$(cat <<-EOF
{
"wormhole": "wormhole.wormhole.testnet",
"codehash": [164, 14, 107, 15, 190, 232, 208, 235, 112, 211, 222, 28, 219, 44, 65, 197, 14, 136, 98, 3, 140, 61, 207, 211, 221, 184, 237, 78, 167, 115, 95, 234],
"initial_source": {
"emitter": [225, 1, 250, 237, 172, 88, 81, 227, 43, 155, 35, 181, 249, 65, 26, 140, 43, 172, 74, 174, 62, 212, 221, 123, 129, 29, 209, 167, 46, 164, 170, 113],
"chain": 26
},
"gov_source": {
"emitter": [86, 53, 151, 154, 34, 28, 52, 147, 30, 50, 98, 11, 146, 147, 164, 99, 6, 85, 85, 234, 113, 254, 151, 205, 98, 55, 173, 232, 117, 177, 46, 158],
"chain": 1
},
"update_fee": "1",
"stale_threshold": 60
}
EOF
)
INIT_JSON=$(echo "$INIT_ARGS" | jq -c '.' -M)
near deploy \
--accountId "780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7" \
--wasmFile pyth.wasm \
--initFunction new \
--initArgs "$INIT_JSON"

View File

@ -0,0 +1 @@
1.69.0-x86_64-unknown-linux-gnu

View File

@ -37,8 +37,8 @@ pub enum Error {
#[error("Governance Module Action not valid.")]
InvalidGovernanceAction,
#[error("Source for attestation is not allowed.")]
UnknownSource,
#[error("Source for attestation is not allowed. {0:?}")]
UnknownSource([u8; 32]),
#[error("Unauthorized Upgrade.")]
UnauthorizedUpgrade,
@ -57,6 +57,18 @@ pub enum Error {
#[error("Unknown error.")]
Unknown,
#[error("Invalid merkle proof.")]
InvalidMerkleProof,
#[error("Invalid accumulator message.")]
InvalidAccumulatorMessage,
#[error("Invalid accumulator message type.")]
InvalidAccumulatorMessageType,
#[error("Invalid wormhole message.")]
InvalidWormholeMessage,
}
/// Convert IO errors into Payload errors, the only I/O we do is parsing with `Cursor` so this is a

View File

@ -1,7 +1,20 @@
//! This module defines external contract API's that are used by the contract. This includes
//! Wormhole and perhaps any ancillary Pyth contracts.
use near_sdk::ext_contract;
use {
crate::{
error::Error,
state::{
Price,
PriceIdentifier,
Source,
},
},
near_sdk::{
ext_contract,
json_types::U128,
},
};
/// Defines the external contract API we care about for interacting with Wormhole. Note that
/// Wormhole on NEAR passes VAA's as hex encoded strings so that the explorer can display them in a
@ -11,3 +24,19 @@ pub trait Wormhole {
/// Returns the Governance Index of the current GuardianSet only if the VAA verifies.
fn verify_vaa(&self, vaa: String) -> u32;
}
/// An external definition of the Pyth interface.
#[ext_contract(ext_pyth)]
pub trait Pyth {
fn update_price_feeds(&mut self, data: String) -> Result<(), Error>;
fn get_update_fee_estimate(&self, vaa: String) -> U128;
fn get_sources(&self) -> Vec<Source>;
fn get_stale_threshold(&self) -> u64;
fn price_feed_exists(&self, price_identifier: PriceIdentifier) -> bool;
fn get_price(&self, price_identifier: PriceIdentifier) -> Option<Price>;
fn get_price_unsafe(&self, price_identifier: PriceIdentifier) -> Option<Price>;
fn get_price_no_older_than(&self, price_id: PriceIdentifier, age: u64) -> Option<Price>;
fn get_ema_price(&self, price_id: PriceIdentifier) -> Option<Price>;
fn get_ema_price_unsafe(&self, price_id: PriceIdentifier) -> Option<Price>;
fn get_ema_price_no_older_than(&self, price_id: PriceIdentifier, age: u64) -> Option<Price>;
}

View File

@ -280,7 +280,7 @@ impl Pyth {
emitter: vaa.emitter_address,
chain: vaa.emitter_chain,
}),
UnknownSource
UnknownSource(vaa.emitter_address)
);
}
@ -500,7 +500,7 @@ impl Pyth {
fn is_valid_governance_source(&self, source: &Source) -> Result<(), Error> {
(self.gov_source == *source)
.then_some(())
.ok_or(UnknownSource)
.ok_or(UnknownSource(source.emitter))
}
}

View File

@ -27,7 +27,25 @@ use {
Promise,
StorageUsage,
},
pyth_wormhole_attester_sdk::BatchPriceAttestation,
pyth_wormhole_attester_sdk::{
BatchPriceAttestation,
P2W_MAGIC,
},
pythnet_sdk::{
accumulators::merkle::MerkleRoot,
hashers::keccak256_160::Keccak160,
messages::Message,
wire::{
from_slice,
v1::{
AccumulatorUpdateData,
Proof,
WormholeMessage,
WormholePayload,
PYTHNET_ACCUMULATOR_UPDATE_MAGIC,
},
},
},
state::{
Price,
PriceFeed,
@ -35,11 +53,15 @@ use {
Source,
Vaa,
},
std::io::Cursor,
std::io::{
Cursor,
Read,
},
};
pub mod error;
pub mod ext;
#[cfg(not(feature = "library"))]
pub mod governance;
pub mod state;
pub mod tests;
@ -53,7 +75,9 @@ enum StorageKeys {
/// The `State` contains all persisted state for the contract. This includes runtime configuration.
///
/// There is no valid Default state for this contract, so we derive PanicOnDefault to force
/// deployment using one of the #[init] functions in the impl below.
/// deployment using one of the #[init] functions in the impl below. We also want to disable
/// this whole definition if the "library" feature flag is on.
#[cfg(not(feature = "library"))]
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
pub struct Pyth {
@ -89,6 +113,7 @@ pub struct Pyth {
update_fee: u128,
}
#[cfg(not(feature = "library"))]
#[near_bindgen]
impl Pyth {
#[init]
@ -129,62 +154,70 @@ impl Pyth {
/// done in a callback handler, see `process_vaa_callback`.
#[payable]
#[handle_result]
pub fn update_price_feed(&mut self, vaa_hex: String) -> Result<(), Error> {
// We Verify the VAA is coming from a trusted source chain before attempting to verify
// VAA signatures. Avoids a cross-contract call early.
let vaa = hex::decode(&vaa_hex).map_err(|_| Error::InvalidHex)?;
let vaa = serde_wormhole::from_slice_with_payload::<wormhole::Vaa<()>>(&vaa);
let vaa = vaa.map_err(|_| Error::InvalidVaa)?;
let (vaa, _rest) = vaa;
pub fn update_price_feeds(&mut self, data: String) -> Result<(), Error> {
// Attempt to deserialize the Payload based on header.
let bytes = &*hex::decode(data.clone()).map_err(|_| Error::InvalidHex)?;
let cursor = &mut Cursor::new(bytes);
let mut header = [0u8; 4];
cursor.clone().read_exact(&mut header).unwrap();
// Convert to local VAA type to catch APi changes.
let vaa = Vaa::from(vaa);
// Handle Accumulator style Price Updates.
if &header == PYTHNET_ACCUMULATOR_UPDATE_MAGIC {
let update_data =
AccumulatorUpdateData::try_from_slice(&cursor.clone().into_inner()).unwrap();
if !self.sources.contains(&Source {
emitter: vaa.emitter_address,
chain: vaa.emitter_chain,
}) {
return Err(Error::UnknownSource);
match update_data.proof {
Proof::WormholeMerkle { vaa, .. } => {
self.verify_encoded_vaa_source(vaa.as_ref())?;
let vaa_hex = hex::encode(vaa.as_ref());
ext_wormhole::ext(self.wormhole.clone())
.with_static_gas(Gas(30_000_000_000_000))
.verify_vaa(vaa_hex.clone())
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(30_000_000_000_000))
.with_attached_deposit(env::attached_deposit())
.verify_wormhole_merkle_callback(
env::predecessor_account_id(),
data,
),
)
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(30_000_000_000_000))
.refund_vaa(env::predecessor_account_id(), env::attached_deposit()),
);
}
};
} else {
// We Verify the VAA is coming from a trusted source chain before attempting to verify
// VAA signatures. Avoids a cross-contract call early.
self.verify_encoded_vaa_source(bytes)?;
// Verify VAA and refund the caller in case of failure.
ext_wormhole::ext(self.wormhole.clone())
.with_static_gas(Gas(30_000_000_000_000))
.verify_vaa(data.clone())
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(30_000_000_000_000))
.with_attached_deposit(env::attached_deposit())
.verify_wormhole_batch_callback(env::predecessor_account_id(), data),
)
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(30_000_000_000_000))
.refund_vaa(env::predecessor_account_id(), env::attached_deposit()),
);
}
// Verify VAA and refund the caller in case of failure.
ext_wormhole::ext(self.wormhole.clone())
.with_static_gas(Gas(30_000_000_000_000))
.verify_vaa(vaa_hex.clone())
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(30_000_000_000_000))
.with_attached_deposit(env::attached_deposit())
.verify_vaa_callback(env::predecessor_account_id(), vaa_hex),
)
.then(
Self::ext(env::current_account_id())
.with_static_gas(Gas(30_000_000_000_000))
.refund_vaa(env::predecessor_account_id(), env::attached_deposit()),
);
Ok(())
}
/// Return the deposit required to update a price feed. This is the upper limit for an update
/// call and any remaining deposit not consumed for storage will be refunded.
#[allow(unused_variables)]
pub fn get_update_fee_estimate(&self, vaa: String) -> U128 {
let byte_cost = env::storage_byte_cost();
let data_cost = byte_cost * std::mem::size_of::<PriceFeed>() as u128;
// The const multiplications here are to provide additional headway for any unexpected data
// costs in NEAR's storage calculations.
//
// 5 is the upper limit for PriceFeed amount in a single update.
// 4 is the value obtained through testing for headway.
(5u128 * 4u128 * data_cost + self.update_fee).into()
}
#[payable]
#[private]
#[handle_result]
pub fn verify_vaa_callback(
pub fn verify_wormhole_batch_callback(
&mut self,
account_id: AccountId,
vaa: String,
@ -214,12 +247,19 @@ impl Pyth {
let (_, rest): (wormhole::Vaa<()>, _) =
serde_wormhole::from_slice_with_payload(&vaa).unwrap();
// Attempt to deserialize the Batch of Price Attestations.
// Attempt to deserialize the Payload based on header.
let bytes = &mut Cursor::new(rest);
let batch = BatchPriceAttestation::deserialize(bytes).unwrap();
let mut header = [0u8; 4];
bytes.clone().read_exact(&mut header).unwrap();
// Check the header is a P2W header and return if not.
if &header != P2W_MAGIC {
return Err(Error::InvalidVaa);
}
// Verify the PriceAttestation's are new enough, and if so, store them.
let mut count_updates = 0;
let batch = BatchPriceAttestation::deserialize(bytes).unwrap();
for price_attestation in &batch.price_attestations {
if self.update_price_feed_if_new(PriceFeed::from(price_attestation)) {
count_updates += 1;
@ -238,7 +278,7 @@ impl Pyth {
"costs": {},
}}
}}
"#,
"#,
count_updates,
env::storage_usage() - storage,
env::storage_byte_cost() * (env::storage_usage() - storage) as u128,
@ -253,6 +293,130 @@ impl Pyth {
)
}
#[payable]
#[private]
#[handle_result]
pub fn verify_wormhole_merkle_callback(
&mut self,
account_id: AccountId,
data: String,
#[callback_result] _result: Result<u32, near_sdk::PromiseError>,
) -> Result<(), Error> {
if !is_promise_success() {
return Err(Error::VaaVerificationFailed);
}
// Get Storage Usage before execution, subtracting the fee from the deposit has the effect
// forces the caller to add the required fee to the deposit. The protocol defines the fee
// as a u128, but storage is a u64, so we need to check that the fee does not overflow the
// storage cost as well.
let storage = (env::storage_usage() as u128)
.checked_sub(
self.update_fee
.checked_div(env::storage_byte_cost())
.ok_or(Error::ArithmeticOverflow)?,
)
.ok_or(Error::InsufficientDeposit)
.and_then(|s| u64::try_from(s).map_err(|_| Error::ArithmeticOverflow))?;
let mut count_updates = 0;
let bytes = &*hex::decode(data.clone()).map_err(|_| Error::InvalidHex)?;
let cursor = &mut Cursor::new(bytes);
let update_data =
AccumulatorUpdateData::try_from_slice(&cursor.clone().into_inner()).unwrap();
match update_data.proof {
Proof::WormholeMerkle { vaa, updates } => {
let (_, rest): (wormhole::Vaa<()>, _) =
serde_wormhole::from_slice_with_payload(vaa.as_ref()).unwrap();
let message = WormholeMessage::try_from_bytes(rest)
.map_err(|_| Error::InvalidWormholeMessage)?;
let root: MerkleRoot<Keccak160> = MerkleRoot::new(match message.payload {
WormholePayload::Merkle(merkle_root) => merkle_root.root,
});
for update in updates {
let message_vec = Vec::from(update.message);
if !root.check(update.proof, &message_vec) {
return Err(Error::InvalidMerkleProof)?;
}
let msg = from_slice::<byteorder::BE, Message>(&message_vec)
.map_err(|_| Error::InvalidAccumulatorMessage)?;
match msg {
Message::PriceFeedMessage(price_feed_message) => {
if self.update_price_feed_if_new(PriceFeed::from(&price_feed_message)) {
count_updates += 1;
}
}
_ => return Err(Error::InvalidAccumulatorMessageType)?,
}
}
}
}
log!(
r#"
{{
"standard": "pyth",
"version": "1.0",
"event": "AccumulatorUpdates",
"data": {{
"count": {},
"diffs": {},
"costs": {},
}}
}}
"#,
count_updates,
env::storage_usage() - storage,
env::storage_byte_cost() * (env::storage_usage() - storage) as u128,
);
// Refund storage difference to `account_id` after storage execution.
self.refund_storage_usage(
account_id,
storage,
env::storage_usage(),
env::attached_deposit(),
)
}
/// Return the deposit required to update a price feed. This is the upper limit for an update
/// call and any remaining deposit not consumed for storage will be refunded.
#[allow(unused_variables)]
pub fn get_update_fee_estimate(&self, data: String) -> U128 {
let byte_cost = env::storage_byte_cost();
let data_cost = byte_cost * std::mem::size_of::<PriceFeed>() as u128;
// If the data is an Accumulator style update, we should count he number of updates being
// calculated in the fee.
let bytes = hex::decode(data).unwrap();
let cursor = &mut Cursor::new(bytes);
let mut header = [0u8; 4];
let mut total_updates = 0u128;
cursor.clone().read_exact(&mut header).unwrap();
if &header == PYTHNET_ACCUMULATOR_UPDATE_MAGIC {
let update_data =
AccumulatorUpdateData::try_from_slice(&cursor.clone().into_inner()).unwrap();
match update_data.proof {
Proof::WormholeMerkle { vaa: _, updates } => {
total_updates += updates.len() as u128;
}
}
} else {
total_updates = 1;
}
// The const multiplications here are to provide additional headway for any unexpected data
// costs in NEAR's storage calculations.
//
// 5 is the upper limit for PriceFeed amount in a single update.
// 4 is the value obtained through testing for headway.
(5u128 * 4u128 * data_cost + self.update_fee * total_updates).into()
}
/// Read the list of accepted `Source` chains for a price attestation.
pub fn get_sources(&self) -> Vec<Source> {
self.sources.iter().collect()
@ -347,7 +511,26 @@ impl Pyth {
/// This second `impl Pyth` block contains only private methods that are called internally that
/// have no transaction semantics associated with them. Note that these do not need `#[private]`
/// annotations as they are already uncallable.
#[cfg(not(feature = "library"))]
impl Pyth {
/// Verify a VAA source from a serialized VAA.
fn verify_encoded_vaa_source(&self, vaa: &[u8]) -> Result<(), Error> {
let (vaa, _): (wormhole::Vaa<()>, _) =
serde_wormhole::from_slice_with_payload(vaa).unwrap();
// Convert to local VAA type to catch API changes.
let vaa = Vaa::from(vaa);
if !self.sources.contains(&Source {
emitter: vaa.emitter_address,
chain: vaa.emitter_chain,
}) {
return Err(Error::UnknownSource(vaa.emitter_address));
}
Ok(())
}
/// Updates the Price Feed only if it is newer than the current one. This function never fails
/// and will either update in-place or not update at all. The return value indicates whether
/// the update was performed or not.
@ -399,6 +582,7 @@ impl Pyth {
}
}
#[cfg(not(feature = "library"))]
#[no_mangle]
pub extern "C" fn update_contract() {
env::setup_panic_hook();

View File

@ -11,6 +11,7 @@ use {
},
},
pyth_wormhole_attester_sdk::PriceAttestation,
pythnet_sdk::messages::PriceFeedMessage,
wormhole::Chain as WormholeChain,
};
@ -80,6 +81,26 @@ impl From<&PriceAttestation> for PriceFeed {
}
}
impl From<&PriceFeedMessage> for PriceFeed {
fn from(price_feed_message: &PriceFeedMessage) -> Self {
Self {
id: PriceIdentifier(price_feed_message.feed_id),
price: Price {
price: price_feed_message.price,
conf: price_feed_message.conf,
expo: price_feed_message.exponent,
timestamp: price_feed_message.publish_time.try_into().unwrap(),
},
ema_price: Price {
price: price_feed_message.ema_price,
conf: price_feed_message.ema_conf,
expo: price_feed_message.exponent,
timestamp: price_feed_message.publish_time.try_into().unwrap(),
},
}
}
}
/// A wrapper around a 16bit chain identifier. We can't use Chain from the Wormhole SDK as it does
/// not provide borsh serialization but we can re-wrap it here relying on the validation from
/// `wormhole::Chain`.

View File

@ -1,11 +1,6 @@
use {
byteorder::BigEndian,
near_sdk::json_types::U128,
pyth_wormhole_attester_sdk::{
BatchPriceAttestation,
Identifier,
PriceAttestation,
PriceStatus,
},
pyth::{
governance::{
GovernanceAction,
@ -19,6 +14,35 @@ use {
Source,
},
},
pyth_wormhole_attester_sdk::{
BatchPriceAttestation,
Identifier,
PriceAttestation,
PriceStatus,
},
pythnet_sdk::{
accumulators::{
merkle::MerkleTree,
Accumulator,
},
hashers::keccak256_160::Keccak160,
messages::{
Message,
PriceFeedMessage,
},
wire::{
to_vec,
v1::{
AccumulatorUpdateData,
MerklePriceUpdate,
Proof,
WormholeMerkleRoot,
WormholeMessage,
WormholePayload,
},
PrefixedVec,
},
},
serde_json::json,
std::io::{
Cursor,
@ -347,21 +371,22 @@ async fn test_stale_threshold() {
cur.write_all(
&BatchPriceAttestation {
price_attestations: vec![PriceAttestation {
product_id: Identifier::default(),
price_id: Identifier::default(),
price: 100,
conf: 1,
expo: 8,
ema_price: 100,
ema_conf: 1,
status: PriceStatus::Trading,
num_publishers: 8,
max_num_publishers: 8,
attestation_time: now.try_into().unwrap(),
publish_time: now.try_into().unwrap(),
prev_publish_time: now.try_into().unwrap(),
prev_price: 100,
prev_conf: 1,
product_id: Identifier::default(),
price_id: Identifier::default(),
price: 100,
conf: 1,
expo: 8,
ema_price: 100,
ema_conf: 1,
status: PriceStatus::Trading,
num_publishers: 8,
max_num_publishers: 8,
attestation_time: now.try_into().unwrap(),
publish_time: now.try_into().unwrap(),
prev_publish_time: now.try_into().unwrap(),
prev_price: 100,
prev_conf: 1,
last_attested_publish_time: now.try_into().unwrap(),
}],
}
.serialize()
@ -389,7 +414,7 @@ async fn test_stale_threshold() {
.gas(300_000_000_000_000)
.deposit(update_fee.into())
.args_json(&json!({
"vaa_hex": vaa,
"data": vaa,
}))
.transact_async()
.await
@ -430,21 +455,22 @@ async fn test_stale_threshold() {
cur.write_all(
&BatchPriceAttestation {
price_attestations: vec![PriceAttestation {
product_id: Identifier::default(),
price_id: Identifier::default(),
price: 1000,
conf: 1,
expo: 8,
ema_price: 1000,
ema_conf: 1,
status: PriceStatus::Trading,
num_publishers: 8,
max_num_publishers: 8,
attestation_time: (now - 1024).try_into().unwrap(),
publish_time: (now - 1024).try_into().unwrap(),
prev_publish_time: (now - 1024).try_into().unwrap(),
prev_price: 90,
prev_conf: 1,
product_id: Identifier::default(),
price_id: Identifier::default(),
price: 1000,
conf: 1,
expo: 8,
ema_price: 1000,
ema_conf: 1,
status: PriceStatus::Trading,
num_publishers: 8,
max_num_publishers: 8,
attestation_time: (now - 1024).try_into().unwrap(),
publish_time: (now - 1024).try_into().unwrap(),
prev_publish_time: (now - 1024).try_into().unwrap(),
prev_price: 90,
prev_conf: 1,
last_attested_publish_time: (now - 1024).try_into().unwrap(),
}],
}
.serialize()
@ -460,7 +486,7 @@ async fn test_stale_threshold() {
.gas(300_000_000_000_000)
.deposit(update_fee.into())
.args_json(&json!({
"vaa_hex": vaa,
"data": vaa,
}))
.transact_async()
.await
@ -647,21 +673,22 @@ async fn test_contract_fees() {
cur.write_all(
&BatchPriceAttestation {
price_attestations: vec![PriceAttestation {
product_id: Identifier::default(),
price_id: Identifier::default(),
price: 1000,
conf: 1,
expo: 8,
ema_price: 1000,
ema_conf: 1,
status: PriceStatus::Trading,
num_publishers: 8,
max_num_publishers: 8,
attestation_time: (now - 1024).try_into().unwrap(),
publish_time: (now - 1024).try_into().unwrap(),
prev_publish_time: (now - 1024).try_into().unwrap(),
prev_price: 90,
prev_conf: 1,
product_id: Identifier::default(),
price_id: Identifier::default(),
price: 1000,
conf: 1,
expo: 8,
ema_price: 1000,
ema_conf: 1,
status: PriceStatus::Trading,
num_publishers: 8,
max_num_publishers: 8,
attestation_time: (now - 1024).try_into().unwrap(),
publish_time: (now - 1024).try_into().unwrap(),
prev_publish_time: (now - 1024).try_into().unwrap(),
prev_price: 90,
prev_conf: 1,
last_attested_publish_time: (now - 1024).try_into().unwrap(),
}],
}
.serialize()
@ -676,7 +703,7 @@ async fn test_contract_fees() {
.gas(300_000_000_000_000)
.deposit(update_fee.into())
.args_json(&json!({
"vaa_hex": vaa,
"data": vaa,
}))
.transact_async()
.await
@ -940,3 +967,164 @@ async fn test_governance_target_fails_if_not_near() {
.failures()
.is_empty());
}
// A test to check accumulator style updates work as intended.
#[tokio::test]
async fn test_accumulator_updates() {
fn create_dummy_price_feed_message(value: i64) -> Message {
let mut dummy_id = [0; 32];
dummy_id[0] = value as u8;
let msg = PriceFeedMessage {
feed_id: dummy_id,
price: value,
conf: value as u64,
exponent: value as i32,
publish_time: value,
prev_publish_time: value,
ema_price: value,
ema_conf: value as u64,
};
Message::PriceFeedMessage(msg)
}
fn create_accumulator_message_from_updates(
price_updates: Vec<MerklePriceUpdate>,
tree: MerkleTree<Keccak160>,
corrupt_wormhole_message: bool,
emitter_address: [u8; 32],
emitter_chain: u16,
) -> Vec<u8> {
let mut root_hash = [0u8; 20];
root_hash.copy_from_slice(&to_vec::<_, BigEndian>(&tree.root).unwrap()[..20]);
let wormhole_message = WormholeMessage::new(WormholePayload::Merkle(WormholeMerkleRoot {
slot: 0,
ring_size: 0,
root: root_hash,
}));
let vaa = wormhole::Vaa {
emitter_chain: emitter_chain.into(),
emitter_address: wormhole::Address(emitter_address),
sequence: 2,
payload: (),
..Default::default()
};
let vaa = {
let mut cur = Cursor::new(Vec::new());
serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
cur.write_all(&to_vec::<_, BigEndian>(&wormhole_message).unwrap())
.expect("Failed to write Payload");
cur.into_inner()
};
let accumulator_update_data = AccumulatorUpdateData::new(Proof::WormholeMerkle {
vaa: PrefixedVec::from(vaa),
updates: price_updates,
});
to_vec::<_, BigEndian>(&accumulator_update_data).unwrap()
}
fn create_accumulator_message(
all_feeds: &[Message],
updates: &[Message],
corrupt_wormhole_message: bool,
) -> Vec<u8> {
let all_feeds_bytes: Vec<_> = all_feeds
.iter()
.map(|f| to_vec::<_, BigEndian>(f).unwrap())
.collect();
let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect();
let tree = MerkleTree::<Keccak160>::new(all_feeds_bytes_refs.as_slice()).unwrap();
let mut price_updates: Vec<MerklePriceUpdate> = vec![];
for update in updates {
let proof = tree
.prove(&to_vec::<_, BigEndian>(update).unwrap())
.unwrap();
price_updates.push(MerklePriceUpdate {
message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()),
proof,
});
}
create_accumulator_message_from_updates(
price_updates,
tree,
corrupt_wormhole_message,
[1; 32],
wormhole::Chain::Any.into(),
)
}
let (_, contract, _) = initialize_chain().await;
// Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
let vaa = wormhole::Vaa {
emitter_chain: wormhole::Chain::Any,
emitter_address: wormhole::Address([0; 32]),
sequence: 1,
payload: (),
..Default::default()
};
let vaa = {
let mut cur = Cursor::new(Vec::new());
serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
cur.write_all(
&GovernanceInstruction {
target: Chain::from(WormholeChain::Any),
module: GovernanceModule::Target,
action: GovernanceAction::SetDataSources {
data_sources: vec![
Source::default(),
Source {
emitter: [1; 32],
chain: Chain::from(WormholeChain::Any),
},
],
},
}
.serialize()
.unwrap(),
)
.expect("Failed to write Payload");
hex::encode(cur.into_inner())
};
assert!(contract
.call("execute_governance_instruction")
.gas(300_000_000_000_000)
.deposit(300_000_000_000_000_000_000_000)
.args_json(&json!({
"vaa": vaa,
}))
.transact_async()
.await
.expect("Failed to submit VAA")
.await
.unwrap()
.failures()
.is_empty());
// Create a couple of test feeds.
let feed_1 = create_dummy_price_feed_message(100);
let feed_2 = create_dummy_price_feed_message(200);
let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1], false);
let message = hex::encode(message);
// Call the usual UpdatePriceFeed function.
assert!(contract
.call("update_price_feed")
.gas(300_000_000_000_000)
.deposit(300_000_000_000_000_000_000_000)
.args_json(&json!({
"data": message,
}))
.transact_async()
.await
.expect("Failed to submit VAA")
.await
.unwrap()
.failures()
.is_empty());
}

View File

@ -0,0 +1,12 @@
# near send 8c77a9896504848e06092618aa166aea4470153c16d38af3902c2ac688eb3a12 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 245e9ccf5f51a6eb08d80095db8623809f0a9a3fc2811c2b316d3b3305c6a4bc 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send f2f8df27296c9d01da063a6cc724dcbadb844ab1490e0b4f0e56995ed2a10239 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send c1aae1fb5b504865ee2748257a6ec4b86b4b0c215c1ecfc0da66f389f5d5c510 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 6c1233da7e91a72f0e298c16d04eb729d8e924a2806d5a339cdb4159aa2e2514 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 192b010686fe6cfca168b2cc21e455b5d10c61037f5ade85b6c791dc81284260 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 597c2ec824154507ecd72997bb041a60d357a103fab163891a7b55e64f62e9ca 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 985fb94522458ec08a400ea8848d6d6ad4a75eb2a7a394ef1d1752050d49460a 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send ce94a02bf130fa5a68daa09fead47d2b6f0768ff16b9ca1311a88ce8c9f4e9ea 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 63c97554b0995d67ebe264730782f7366494207a635b3684270c3523865a501a 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
# near send 879ad801aedfaeaf1924ce3e31e8111ee04548703c922b25a3eae67ea2887776 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 9.9
near send 83f84e22f92089bef6a2ae443626c92e894f10a46b426ffd575e9ea14da2a3f2 780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 200

View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
UPDATE_ARGS=$(
cat <<-EOF
{
"data": "504e41550100000000a0010000000001008918ed9b917292532426a110f9227ae4b3ef5aeb5cda0909e63f8fbccd7e9db742b70049436800ef44a686199ebcf2a778dfcf0bed52363b6a5c4eb8463f5db901654a687d00000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71000000000217fb2e01415557560000000000067f895500002710b2b3fe19d295ea084ee534ab1c39162192ec230f01005500f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b00000328f52de1c000000000280de7fffffffff800000000654a687c00000000654a687c00000328e5f95d600000000036a473160a71021fa245677cb9e85bc0c46f68cfeb28123895c77156a0f8fbba3212d3d7d4ffdf9ff57856daa44d7127cdfb2295480839569025b5ce5b362de78e85eb029fd12640dde384d0bccce55b09d0bfd3cfa5171d0d08b93403b2b6b97808e7ef8146e8bf96aa1792ee7bd4d305a8b2d59a54a240f6fee3079a6afbac996283ff6fac58d39263ae502c66a4c1a95c1ef7aa8a171bf11ec4480d8a9bae68dd3b67c7ef9f547826dbb8af0466290b3509d389064c8c97739cc9afcd1beb090e85e1326274725b281f0b39"
}
EOF
)
UPDATE_JSON=$(echo "$UPDATE_ARGS" | jq -c '.' -M)
near call \
--accountId "780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7" \
780e82bd52465f8a4f5ed8cf5a30666eb41208849956bf87a377da9d8174e2b7 \
update_price_feed \
--gas 300000000000000 \
--deposit 1 \
--args "$UPDATE_JSON"

View File

@ -0,0 +1 @@
1.69.0-x86_64-unknown-linux-gnu