feat(near): accumulators
This commit is contained in:
parent
16832aba51
commit
9d8c271607
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
1.69.0-x86_64-unknown-linux-gnu
|
|
@ -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.
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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"
|
|
@ -0,0 +1 @@
|
|||
1.69.0-x86_64-unknown-linux-gnu
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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"
|
|
@ -0,0 +1 @@
|
|||
1.69.0-x86_64-unknown-linux-gnu
|
Loading…
Reference in New Issue