diff --git a/Cargo.lock b/Cargo.lock index 9372e45..3727234 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,44 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-primitives" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4885c1409b6936c4898e646ef58baf6ec54edaf6d8179f79df805a7b85b7cf3e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash 0.2.0", + "hashbrown 0.17.1", + "indexmap 2.14.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.4", + "rapidhash", + "ruint", + "rustc-hash", + "secp256k1", + "serde", + "sha3", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "amm-methods" version = "0.1.0" @@ -81,9 +119,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-r1cs-std", - "ark-std", + "ark-std 0.5.0", ] [[package]] @@ -95,14 +133,14 @@ dependencies = [ "ahash", "ark-crypto-primitives-macros", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-serialize", + "ark-serialize 0.5.0", "ark-snark", - "ark-std", + "ark-std 0.5.0", "blake2", "derivative", - "digest", + "digest 0.10.7", "fnv", "merlin", "sha2", @@ -126,10 +164,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash", - "ark-ff", + "ark-ff 0.5.0", "ark-poly", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -140,18 +178,56 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "arrayvec", - "digest", + "digest 0.10.7", "educe", "itertools 0.13.0", "num-bigint", @@ -160,6 +236,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.5.0" @@ -170,6 +266,31 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.5.0" @@ -191,11 +312,11 @@ checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" dependencies = [ "ark-crypto-primitives", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-poly", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", ] [[package]] @@ -205,9 +326,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ "ahash", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -220,9 +341,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-std", + "ark-std 0.5.0", "educe", "num-bigint", "num-integer", @@ -236,12 +357,33 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" dependencies = [ - "ark-ff", - "ark-std", + "ark-ff 0.5.0", + "ark-std 0.5.0", "tracing", "tracing-subscriber", ] +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + [[package]] name = "ark-serialize" version = "0.5.0" @@ -249,9 +391,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ "ark-serialize-derive", - "ark-std", + "ark-std 0.5.0", "arrayvec", - "digest", + "digest 0.10.7", "num-bigint", ] @@ -272,10 +414,30 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" dependencies = [ - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.6", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.6", ] [[package]] @@ -334,11 +496,22 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base16ct" @@ -373,12 +546,37 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-io" +version = "0.1.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -391,13 +589,25 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -472,9 +682,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" @@ -546,7 +762,7 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.28", "serde", "serde_json", "thiserror 2.0.18", @@ -599,12 +815,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", ] @@ -626,12 +842,54 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "const-hex" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -677,6 +935,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -701,9 +965,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] @@ -855,13 +1119,23 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.117", "unicode-xid", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" @@ -874,6 +1148,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", +] + [[package]] name = "dirs" version = "6.0.0" @@ -892,14 +1176,14 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -942,7 +1226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "serdect", @@ -964,9 +1248,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elf" @@ -982,7 +1266,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -1049,7 +1333,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", ] [[package]] @@ -1058,6 +1352,28 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.1" @@ -1074,6 +1390,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.6", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1086,6 +1414,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1122,6 +1456,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures-channel" version = "0.3.32" @@ -1258,7 +1598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1266,6 +1606,9 @@ name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -1288,6 +1631,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -1300,14 +1652,14 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1353,9 +1705,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" dependencies = [ "atomic-waker", "bytes", @@ -1558,12 +1910,38 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "include_bytes_aligned" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + [[package]] name = "indexmap" version = "1.9.3" @@ -1618,6 +1996,15 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1644,9 +2031,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -1678,6 +2065,41 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + +[[package]] +name = "keccak-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1766b89733097006f3a1388a02849865d6bc98c89273cb622e29fdd209922183" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy-regex" version = "3.6.0" @@ -1730,9 +2152,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -1751,9 +2173,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lru-slab" @@ -1772,9 +2194,9 @@ dependencies = [ [[package]] name = "maybe-async" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", @@ -1783,9 +2205,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "merlin" @@ -1794,7 +2216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", - "keccak", + "keccak 0.1.6", "rand_core 0.6.4", "zeroize", ] @@ -1898,9 +2320,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1974,6 +2396,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "paste" version = "1.0.15" @@ -1995,6 +2445,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2068,13 +2528,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -2104,11 +2575,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ + "bit-set", + "bit-vec", "bitflags 2.11.1", "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", "unarray", ] @@ -2135,6 +2611,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quinn" version = "0.11.9" @@ -2211,6 +2693,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.6" @@ -2279,6 +2767,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rustversion", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -2422,7 +2919,7 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "ruint", - "semver", + "semver 1.0.28", "serde", "tracing", ] @@ -2444,7 +2941,7 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "rzup", - "semver", + "semver 1.0.28", "serde", "serde_json", "stability", @@ -2519,9 +3016,9 @@ dependencies = [ "anyhow", "ark-bn254", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-groth16", - "ark-serialize", + "ark-serialize 0.5.0", "bytemuck", "hex", "num-bigint", @@ -2553,7 +3050,7 @@ dependencies = [ "borsh", "bytemuck", "cfg-if", - "digest", + "digest 0.10.7", "hex", "hex-literal", "metal", @@ -2595,7 +3092,7 @@ dependencies = [ "risc0-zkvm-platform", "rrs-lib", "rzup", - "semver", + "semver 1.0.28", "serde", "sha2", "stability", @@ -2619,6 +3116,16 @@ dependencies = [ "stability", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -2636,7 +3143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", - "digest", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -2651,14 +3158,27 @@ dependencies = [ [[package]] name = "ruint" -version = "1.18.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", "borsh", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", "proptest", "rand 0.8.6", "rand 0.9.4", + "rlp", "ruint-macro", "serde_core", "valuable", @@ -2677,13 +3197,28 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.28", ] [[package]] @@ -2696,7 +3231,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2740,6 +3275,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.23" @@ -2754,7 +3301,7 @@ checksum = "5d2aed296f203fa64bcb4b52069356dd86d6ec578593985b919b6995bee1f0ae" dependencies = [ "hex", "rsa", - "semver", + "semver 1.0.28", "serde", "serde_with", "sha2", @@ -2804,6 +3351,35 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.4", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.28" @@ -2814,6 +3390,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -2846,9 +3431,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -2928,7 +3513,27 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", +] + +[[package]] +name = "sha3-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3f15d4e239ebe08413eed880e0f9b5af4b40ee0472543320efa91d488e96a7" +dependencies = [ + "cc", + "cfg-if", ] [[package]] @@ -2943,7 +3548,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -2966,7 +3571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -3059,6 +3664,12 @@ dependencies = [ "token_core", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" @@ -3134,6 +3745,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.27.0" @@ -3144,7 +3761,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3353,14 +3970,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3369,7 +3986,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3395,9 +4012,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags 2.11.1", "bytes", @@ -3484,16 +4101,21 @@ dependencies = [ name = "twap_oracle_core" version = "0.1.0" dependencies = [ + "alloy-primitives", "borsh", "nssa_core", + "risc0-zkvm", + "ruint", "serde", "spel-framework-macros", + "uniswap_v3_math", ] [[package]] name = "twap_oracle_program" version = "0.1.0" dependencies = [ + "clock_core", "nssa_core", "twap_oracle_core", ] @@ -3504,6 +4126,24 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -3516,12 +4156,29 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "uniswap_v3_math" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e393498a831893ce69ed6e1d06615e400bd1e8f97e9fcd113324f2d610fe6d45" +dependencies = [ + "alloy-primitives", + "eyre", + "thiserror 2.0.18", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -3558,6 +4215,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "want" version = "0.3.1" @@ -3593,9 +4259,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3606,9 +4272,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3616,9 +4282,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3626,9 +4292,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3639,9 +4305,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3690,14 +4356,14 @@ dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap 2.14.0", - "semver", + "semver 1.0.28", ] [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -3948,9 +4614,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -4041,7 +4707,7 @@ dependencies = [ "id-arena", "indexmap 2.14.0", "log", - "semver", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -4055,6 +4721,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yaml-rust2" version = "0.10.4" @@ -4091,18 +4766,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", diff --git a/artifacts/stablecoin-idl.json b/artifacts/stablecoin-idl.json index 5c601e8..1c3d9e8 100644 --- a/artifacts/stablecoin-idl.json +++ b/artifacts/stablecoin-idl.json @@ -260,6 +260,38 @@ ] } }, + { + "name": "PriceObservations", + "type": { + "kind": "struct", + "fields": [ + { + "name": "price_source_id", + "type": "account_id" + }, + { + "name": "write_index", + "type": "u32" + }, + { + "name": "total_entries", + "type": "u64" + }, + { + "name": "last_recorded_tick", + "type": "i32" + }, + { + "name": "entries", + "type": { + "vec": { + "defined": "ObservationEntry" + } + } + } + ] + } + }, { "name": "OraclePriceAccount", "type": { @@ -283,7 +315,7 @@ }, { "name": "source_id", - "type": "string" + "type": "account_id" }, { "name": "confidence_interval", @@ -291,6 +323,22 @@ } ] } + }, + { + "name": "CurrentTickAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "tick", + "type": "i32" + }, + { + "name": "last_updated", + "type": "u64" + } + ] + } } ], "types": [ @@ -305,6 +353,20 @@ "name": "Expanded" } ] + }, + { + "name": "ObservationEntry", + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "u64" + }, + { + "name": "tick_cumulative", + "type": "i64" + } + ] } ], "instruction_type": "stablecoin_core::Instruction" diff --git a/artifacts/twap_oracle-idl.json b/artifacts/twap_oracle-idl.json index 59e2279..8c605d4 100644 --- a/artifacts/twap_oracle-idl.json +++ b/artifacts/twap_oracle-idl.json @@ -3,12 +3,227 @@ "name": "twap_oracle", "instructions": [ { - "name": "noop", - "accounts": [], - "args": [] + "name": "create_price_observations", + "accounts": [ + { + "name": "price_observations", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "price_source", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "clock", + "writable": false, + "signer": false, + "init": false + } + ], + "args": [ + { + "name": "initial_tick", + "type": "i32" + }, + { + "name": "window_duration", + "type": "u64" + } + ] + }, + { + "name": "create_oracle_price_account", + "accounts": [ + { + "name": "oracle_price_account", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "price_source", + "writable": false, + "signer": false, + "init": false + } + ], + "args": [ + { + "name": "base_asset", + "type": "account_id" + }, + { + "name": "quote_asset", + "type": "account_id" + }, + { + "name": "window_duration", + "type": "u64" + } + ] + }, + { + "name": "create_current_tick_account", + "accounts": [ + { + "name": "current_tick_account", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "price_source", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "clock", + "writable": false, + "signer": false, + "init": false + } + ], + "args": [ + { + "name": "initial_tick", + "type": "i32" + } + ] + }, + { + "name": "publish_price", + "accounts": [ + { + "name": "price_observations", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "oracle_price_account", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "clock", + "writable": false, + "signer": false, + "init": false + } + ], + "args": [ + { + "name": "price_source_id", + "type": "account_id" + }, + { + "name": "window_duration", + "type": "u64" + } + ] + }, + { + "name": "record_tick", + "accounts": [ + { + "name": "price_observations", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "current_tick_account", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "clock", + "writable": false, + "signer": false, + "init": false + } + ], + "args": [ + { + "name": "price_source_id", + "type": "account_id" + }, + { + "name": "window_duration", + "type": "u64" + } + ] + }, + { + "name": "update_current_tick", + "accounts": [ + { + "name": "current_tick_account", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "price_source", + "writable": false, + "signer": false, + "init": false + }, + { + "name": "clock", + "writable": false, + "signer": false, + "init": false + } + ], + "args": [ + { + "name": "tick", + "type": "i32" + } + ] } ], "accounts": [ + { + "name": "PriceObservations", + "type": { + "kind": "struct", + "fields": [ + { + "name": "price_source_id", + "type": "account_id" + }, + { + "name": "write_index", + "type": "u32" + }, + { + "name": "total_entries", + "type": "u64" + }, + { + "name": "last_recorded_tick", + "type": "i32" + }, + { + "name": "entries", + "type": { + "vec": { + "defined": "ObservationEntry" + } + } + } + ] + } + }, { "name": "OraclePriceAccount", "type": { @@ -32,7 +247,7 @@ }, { "name": "source_id", - "type": "string" + "type": "account_id" }, { "name": "confidence_interval", @@ -40,6 +255,38 @@ } ] } + }, + { + "name": "CurrentTickAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "tick", + "type": "i32" + }, + { + "name": "last_updated", + "type": "u64" + } + ] + } + } + ], + "types": [ + { + "name": "ObservationEntry", + "kind": "struct", + "fields": [ + { + "name": "timestamp", + "type": "u64" + }, + { + "name": "tick_cumulative", + "type": "i64" + } + ] } ], "instruction_type": "twap_oracle_core::Instruction" diff --git a/programs/amm/methods/guest/Cargo.lock b/programs/amm/methods/guest/Cargo.lock index 7dcd666..16a38c0 100644 --- a/programs/amm/methods/guest/Cargo.lock +++ b/programs/amm/methods/guest/Cargo.lock @@ -313,9 +313,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base16ct" @@ -449,9 +449,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -576,12 +576,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", ] @@ -669,9 +669,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] @@ -865,9 +865,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -932,9 +932,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elf" @@ -1273,9 +1273,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1321,9 +1321,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" dependencies = [ "atomic-waker", "bytes", @@ -1587,9 +1587,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -1673,9 +1673,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -1694,9 +1694,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lru-slab" @@ -1715,9 +1715,9 @@ dependencies = [ [[package]] name = "maybe-async" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", @@ -1726,9 +1726,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "merlin" @@ -1819,9 +1819,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1995,7 +1995,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -2766,9 +2766,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3238,14 +3238,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3254,7 +3254,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3280,9 +3280,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags 2.11.1", "bytes", @@ -3451,9 +3451,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3464,9 +3464,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3474,9 +3474,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3484,9 +3484,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3497,9 +3497,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3553,9 +3553,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -3806,9 +3806,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -3949,18 +3949,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", @@ -3969,9 +3969,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] diff --git a/programs/ata/methods/guest/Cargo.lock b/programs/ata/methods/guest/Cargo.lock index ba53cf4..6dacf39 100644 --- a/programs/ata/methods/guest/Cargo.lock +++ b/programs/ata/methods/guest/Cargo.lock @@ -311,9 +311,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base16ct" @@ -447,9 +447,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -574,12 +574,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", ] @@ -667,9 +667,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] @@ -863,9 +863,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -930,9 +930,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elf" @@ -1271,9 +1271,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1319,9 +1319,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" dependencies = [ "atomic-waker", "bytes", @@ -1585,9 +1585,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -1671,9 +1671,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -1692,9 +1692,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lru-slab" @@ -1713,9 +1713,9 @@ dependencies = [ [[package]] name = "maybe-async" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", @@ -1724,9 +1724,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "merlin" @@ -1817,9 +1817,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1993,7 +1993,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -2764,9 +2764,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3236,14 +3236,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3252,7 +3252,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3278,9 +3278,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags 2.11.1", "bytes", @@ -3449,9 +3449,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3462,9 +3462,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3472,9 +3472,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3482,9 +3482,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3495,9 +3495,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3551,9 +3551,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -3804,9 +3804,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -3947,18 +3947,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", @@ -3967,9 +3967,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] diff --git a/programs/stablecoin/methods/guest/Cargo.lock b/programs/stablecoin/methods/guest/Cargo.lock index a382086..952f359 100644 --- a/programs/stablecoin/methods/guest/Cargo.lock +++ b/programs/stablecoin/methods/guest/Cargo.lock @@ -29,6 +29,44 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-primitives" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4885c1409b6936c4898e646ef58baf6ec54edaf6d8179f79df805a7b85b7cf3e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash 0.2.0", + "hashbrown 0.17.1", + "indexmap 2.14.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.4", + "rapidhash", + "ruint", + "rustc-hash", + "secp256k1", + "serde", + "sha3", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -51,9 +89,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-r1cs-std", - "ark-std", + "ark-std 0.5.0", ] [[package]] @@ -65,14 +103,14 @@ dependencies = [ "ahash", "ark-crypto-primitives-macros", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-serialize", + "ark-serialize 0.5.0", "ark-snark", - "ark-std", + "ark-std 0.5.0", "blake2", "derivative", - "digest", + "digest 0.10.7", "fnv", "merlin", "sha2", @@ -96,10 +134,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash", - "ark-ff", + "ark-ff 0.5.0", "ark-poly", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -110,18 +148,56 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "arrayvec", - "digest", + "digest 0.10.7", "educe", "itertools 0.13.0", "num-bigint", @@ -130,6 +206,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.5.0" @@ -140,6 +236,31 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.5.0" @@ -161,11 +282,11 @@ checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" dependencies = [ "ark-crypto-primitives", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-poly", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", ] [[package]] @@ -175,9 +296,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ "ahash", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -190,9 +311,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-std", + "ark-std 0.5.0", "educe", "num-bigint", "num-integer", @@ -206,12 +327,33 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" dependencies = [ - "ark-ff", - "ark-std", + "ark-ff 0.5.0", + "ark-std 0.5.0", "tracing", "tracing-subscriber", ] +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + [[package]] name = "ark-serialize" version = "0.5.0" @@ -219,9 +361,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ "ark-serialize-derive", - "ark-std", + "ark-std 0.5.0", "arrayvec", - "digest", + "digest 0.10.7", "num-bigint", ] @@ -242,10 +384,30 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" dependencies = [ - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.6", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.6", ] [[package]] @@ -276,11 +438,22 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base16ct" @@ -315,12 +488,37 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-io" +version = "0.1.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -333,13 +531,25 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -414,9 +624,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" @@ -488,7 +704,7 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.28", "serde", "serde_json", "thiserror 2.0.18", @@ -541,12 +757,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", ] @@ -559,12 +775,54 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "const-hex" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -610,6 +868,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -634,9 +898,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] @@ -788,13 +1052,23 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.117", "unicode-xid", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" @@ -807,6 +1081,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", +] + [[package]] name = "dirs" version = "6.0.0" @@ -830,9 +1114,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -875,7 +1159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "serdect", @@ -897,9 +1181,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elf" @@ -915,7 +1199,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -985,12 +1269,44 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.1" @@ -1007,6 +1323,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.6", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1019,6 +1347,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1055,6 +1389,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures-channel" version = "0.3.32" @@ -1191,7 +1531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1199,6 +1539,9 @@ name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -1221,6 +1564,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -1233,14 +1585,14 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1286,9 +1638,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" dependencies = [ "atomic-waker", "bytes", @@ -1482,12 +1834,38 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "include_bytes_aligned" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + [[package]] name = "indexmap" version = "1.9.3" @@ -1526,6 +1904,15 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1552,9 +1939,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -1586,6 +1973,41 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + +[[package]] +name = "keccak-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1766b89733097006f3a1388a02849865d6bc98c89273cb622e29fdd209922183" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy-regex" version = "3.6.0" @@ -1638,9 +2060,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -1659,9 +2081,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lru-slab" @@ -1680,9 +2102,9 @@ dependencies = [ [[package]] name = "maybe-async" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", @@ -1691,9 +2113,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "merlin" @@ -1702,7 +2124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", - "keccak", + "keccak 0.1.6", "rand_core 0.6.4", "zeroize", ] @@ -1784,9 +2206,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1860,6 +2282,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "paste" version = "1.0.15" @@ -1881,6 +2331,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1954,13 +2414,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -1990,11 +2461,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ + "bit-set", + "bit-vec", "bitflags 2.11.1", "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", "unarray", ] @@ -2021,6 +2497,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quinn" version = "0.11.9" @@ -2097,12 +2579,19 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ + "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -2164,6 +2653,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rustversion", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -2307,7 +2805,7 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "ruint", - "semver", + "semver 1.0.28", "serde", "tracing", ] @@ -2329,7 +2827,7 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "rzup", - "semver", + "semver 1.0.28", "serde", "serde_json", "stability", @@ -2404,9 +2902,9 @@ dependencies = [ "anyhow", "ark-bn254", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-groth16", - "ark-serialize", + "ark-serialize 0.5.0", "bytemuck", "hex", "num-bigint", @@ -2438,7 +2936,7 @@ dependencies = [ "borsh", "bytemuck", "cfg-if", - "digest", + "digest 0.10.7", "hex", "hex-literal", "metal", @@ -2480,7 +2978,7 @@ dependencies = [ "risc0-zkvm-platform", "rrs-lib", "rzup", - "semver", + "semver 1.0.28", "serde", "sha2", "stability", @@ -2504,6 +3002,16 @@ dependencies = [ "stability", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -2521,7 +3029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", - "digest", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -2536,14 +3044,27 @@ dependencies = [ [[package]] name = "ruint" -version = "1.18.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", "borsh", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", "proptest", "rand 0.8.6", "rand 0.9.4", + "rlp", "ruint-macro", "serde_core", "valuable", @@ -2562,13 +3083,28 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.28", ] [[package]] @@ -2625,6 +3161,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.23" @@ -2639,7 +3187,7 @@ checksum = "5d2aed296f203fa64bcb4b52069356dd86d6ec578593985b919b6995bee1f0ae" dependencies = [ "hex", "rsa", - "semver", + "semver 1.0.28", "serde", "serde_with", "sha2", @@ -2689,6 +3237,35 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.4", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.28" @@ -2699,6 +3276,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -2731,9 +3317,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -2813,7 +3399,27 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", +] + +[[package]] +name = "sha3-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3f15d4e239ebe08413eed880e0f9b5af4b40ee0472543320efa91d488e96a7" +dependencies = [ + "cc", + "cfg-if", ] [[package]] @@ -2828,7 +3434,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -2962,6 +3568,12 @@ dependencies = [ "token_core", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" @@ -3037,6 +3649,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.27.0" @@ -3239,14 +3857,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3255,7 +3873,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3281,9 +3899,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags 2.11.1", "bytes", @@ -3361,10 +3979,14 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" name = "twap_oracle_core" version = "0.1.0" dependencies = [ + "alloy-primitives", "borsh", "nssa_core", + "risc0-zkvm", + "ruint", "serde", "spel-framework-macros", + "uniswap_v3_math", ] [[package]] @@ -3373,6 +3995,24 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -3385,12 +4025,29 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "uniswap_v3_math" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e393498a831893ce69ed6e1d06615e400bd1e8f97e9fcd113324f2d610fe6d45" +dependencies = [ + "alloy-primitives", + "eyre", + "thiserror 2.0.18", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -3427,6 +4084,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "want" version = "0.3.1" @@ -3462,9 +4128,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3475,9 +4141,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3485,9 +4151,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3495,9 +4161,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3508,9 +4174,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3559,14 +4225,14 @@ dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap 2.14.0", - "semver", + "semver 1.0.28", ] [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -3817,9 +4483,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -3910,7 +4576,7 @@ dependencies = [ "id-arena", "indexmap 2.14.0", "log", - "semver", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -3924,6 +4590,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yaml-rust2" version = "0.10.4" @@ -3960,18 +4635,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", diff --git a/programs/token/methods/guest/Cargo.lock b/programs/token/methods/guest/Cargo.lock index 07f88f1..2be20b6 100644 --- a/programs/token/methods/guest/Cargo.lock +++ b/programs/token/methods/guest/Cargo.lock @@ -278,9 +278,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base16ct" @@ -414,9 +414,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -541,12 +541,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "inout", ] @@ -634,9 +634,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] @@ -830,9 +830,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -897,9 +897,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elf" @@ -1238,9 +1238,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1286,9 +1286,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" dependencies = [ "atomic-waker", "bytes", @@ -1552,9 +1552,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -1638,9 +1638,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -1659,9 +1659,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lru-slab" @@ -1680,9 +1680,9 @@ dependencies = [ [[package]] name = "maybe-async" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +checksum = "746873a384ad60adc5db74471dfaba74bd278afbdcfd81db93fafcdfc8b5ca0c" dependencies = [ "proc-macro2", "quote", @@ -1691,9 +1691,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "merlin" @@ -1784,9 +1784,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -1960,7 +1960,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -2731,9 +2731,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3224,14 +3224,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3240,7 +3240,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -3266,9 +3266,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "bitflags 2.11.1", "bytes", @@ -3437,9 +3437,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -3450,9 +3450,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3460,9 +3460,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3470,9 +3470,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -3483,9 +3483,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -3539,9 +3539,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -3792,9 +3792,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -3935,18 +3935,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", @@ -3955,9 +3955,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] diff --git a/programs/twap_oracle/Cargo.toml b/programs/twap_oracle/Cargo.toml index ca8d30e..1637548 100644 --- a/programs/twap_oracle/Cargo.toml +++ b/programs/twap_oracle/Cargo.toml @@ -5,4 +5,8 @@ edition = "2021" [dependencies] nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3", features = ["host"] } +clock_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" } twap_oracle_core = { path = "core" } + +[lints] +workspace = true diff --git a/programs/twap_oracle/core/Cargo.toml b/programs/twap_oracle/core/Cargo.toml index c1a749f..55166ac 100644 --- a/programs/twap_oracle/core/Cargo.toml +++ b/programs/twap_oracle/core/Cargo.toml @@ -3,8 +3,18 @@ name = "twap_oracle_core" version = "0.1.0" edition = "2021" +[lints] +workspace = true + [dependencies] nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3", features = ["host"] } borsh = { version = "1.5", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } spel-framework-macros = { git = "https://github.com/logos-co/spel.git", tag = "v0.3.0", package = "spel-framework-macros" } +risc0-zkvm = { version = "=3.0.5", default-features = false } +uniswap_v3_math = "0.6.2" +alloy-primitives = { version = "1", default-features = false } +# Pin ruint (transitive via alloy-primitives) below 1.18, which raised its MSRV to rustc 1.90. +# The risc0 guest toolchain ships rustc 1.88, so 1.18+ fails the guest build. 1.17.0 (MSRV 1.85) +# is the newest compatible release. Remove this pin once the risc0 toolchain advances past 1.90. +ruint = { version = "=1.17.0", default-features = false } diff --git a/programs/twap_oracle/core/src/lib.rs b/programs/twap_oracle/core/src/lib.rs index f2ac4c0..b12e389 100644 --- a/programs/twap_oracle/core/src/lib.rs +++ b/programs/twap_oracle/core/src/lib.rs @@ -1,18 +1,317 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use nssa_core::account::{AccountId, Data}; +use nssa_core::{ + account::{AccountId, Data}, + program::{PdaSeed, ProgramId}, +}; use serde::{Deserialize, Serialize}; use spel_framework_macros::account_type; /// TWAP Oracle Program Instruction. #[derive(Debug, Serialize, Deserialize)] pub enum Instruction { - /// No-op instruction. Does nothing and returns no state changes. - Noop, + /// Creates and initialises a price observations account for a price source and time window. + /// + /// Required accounts (in order): + /// 1. Price observations account — uninitialized PDA derived from + /// `compute_price_observations_pda(self_program_id, price_source.account_id, + /// window_duration)`. + /// 2. Price source account — the account whose ID acts as the feed identifier (e.g. an AMM + /// pool account); must be passed with `is_authorized = true` to prove the caller controls + /// it. + /// 3. Clock account — read-only; supplies the initial observation timestamp. + CreatePriceObservations { + /// Initial price tick: `floor(log_{1.0001}(reserve_b / reserve_a))`. + initial_tick: i32, + /// Duration of the TWAP window this feed serves, in milliseconds. + /// + /// Together with `OBSERVATIONS_CAPACITY` this determines the minimum sampling interval + /// enforced by `RecordPrice`: `min_interval = window_duration / OBSERVATIONS_CAPACITY`. + /// It is also part of the PDA seed, so each window gets a distinct account. + window_duration: u64, + }, + /// Creates and initialises a canonical [`OraclePriceAccount`] for a price source and time + /// window. + /// + /// The price and timestamp start at zero and are populated later by a `PublishPrice` + /// instruction. Consumers must reject accounts whose `timestamp` is zero or stale. + /// + /// Required accounts (in order): + /// 1. Oracle price account — uninitialized PDA derived from + /// `compute_oracle_price_account_pda(self_program_id, price_source.account_id, + /// window_duration)`. + /// 2. Price source account — must be passed with `is_authorized = true` to prove the caller + /// controls it. Its ID ties this price account to the same source as the corresponding + /// [`PriceObservations`] account for the same window. + CreateOraclePriceAccount { + /// Canonical identifier of the base asset being priced. + base_asset: AccountId, + /// Canonical identifier of the quote asset that denominates `price`. + quote_asset: AccountId, + /// Duration of the TWAP window this price account serves, in milliseconds. + /// + /// Part of the PDA seed, so each `(price_source, window)` pair maps to a distinct + /// oracle price account. + window_duration: u64, + }, + /// Creates and initialises a [`CurrentTickAccount`] for a price source. + /// + /// Called once per price source (not per window). The account holds the latest raw tick + /// written by the price source and serves as the input to `RecordTick`. + /// + /// Required accounts (in order): + /// 1. Current tick account — uninitialized PDA derived from + /// `compute_current_tick_account_pda(self_program_id, price_source.account_id)`. + /// 2. Price source account — must be passed with `is_authorized = true`. + /// 3. Clock account — read-only; supplies the initial timestamp. + CreateCurrentTickAccount { + /// Opening tick: `floor(log_{1.0001}(reserve_b / reserve_a))` at creation time. + initial_tick: i32, + }, + /// Updates the tick stored in an existing [`CurrentTickAccount`]. + /// + /// Called by the price source (e.g. AMM) after each price-changing operation. Anyone may + /// subsequently call `RecordTick` to advance the [`PriceObservations`] accumulator using + /// the new tick. + /// + /// Required accounts (in order): + /// 1. Current tick account — initialized PDA derived from + /// `compute_current_tick_account_pda(self_program_id, price_source.account_id)`. + /// 2. Price source account — must be passed with `is_authorized = true`. + /// 3. Clock account — read-only; supplies the updated timestamp. + UpdateCurrentTick { + /// New raw tick from the price source. + tick: i32, + }, + /// Computes the TWAP over `window_duration` from the [`PriceObservations`] ring buffer and + /// writes the result to the [`OraclePriceAccount`]. + /// + /// Permissionless — anyone may call this. Returns all accounts unchanged (no-op) if the + /// ring buffer holds fewer than two observations. Once at least two observations exist the + /// TWAP is computed over the available history, which may be shorter than `window_duration` + /// while the buffer is young. + /// + /// The resulting TWAP tick is stored in [`OraclePriceAccount::price`] via + /// [`tick_to_oracle_price`]. Consumers decode with [`oracle_price_to_tick`]. + /// + /// Required accounts (in order): + /// 1. Price observations account — initialized PDA derived from + /// `compute_price_observations_pda(self_program_id, price_source_id, window_duration)`. + /// 2. Oracle price account — initialized PDA derived from + /// `compute_oracle_price_account_pda(self_program_id, price_source_id, window_duration)`. + /// 3. Clock account — read-only; supplies the publication timestamp. + PublishPrice { + /// ID of the price source; used to verify both PDAs. + price_source_id: AccountId, + /// Duration of the TWAP window in milliseconds; used to verify both PDAs and to + /// locate the boundary observation in the ring buffer. + window_duration: u64, + }, + /// Records the current tick from a [`CurrentTickAccount`] into a [`PriceObservations`] + /// ring buffer. + /// + /// Permissionless — anyone may call this. Both PDAs are verified against `price_source_id`, + /// so the tick can only have been written by whoever controls that price source. + /// + /// A sampling guard silently skips the write if less than + /// `window_duration / OBSERVATIONS_CAPACITY` milliseconds have elapsed since the last + /// observation. Callers may call this on every block without concern — the guard handles + /// downsampling on-chain. + /// + /// Required accounts (in order): + /// 1. Price observations account — initialized PDA derived from + /// `compute_price_observations_pda(self_program_id, price_source_id, window_duration)`. + /// 2. Current tick account — initialized PDA derived from + /// `compute_current_tick_account_pda(self_program_id, price_source_id)`. + /// 3. Clock account — read-only; supplies the current timestamp. + RecordTick { + /// ID of the price source; used to verify both PDAs. + price_source_id: AccountId, + /// Duration of the TWAP window in milliseconds; used to verify the + /// [`PriceObservations`] PDA and to compute the sampling guard interval. + window_duration: u64, + }, +} + +// ────────────────────────────────────────────────────────────────────────────── +// Price feed +// ────────────────────────────────────────────────────────────────────────────── + +/// Maximum tick delta injected into the accumulator per observation. +/// +/// Matches the Uniswap v4 truncated oracle hook reference value (~2.39× price move per block). +/// An attacker who moves the pool by more than this in one block still only injects +/// `MAX_TICK_DELTA` ticks into the cumulative — they must sustain the manipulation across +/// many blocks while arbitrage erodes their position. +pub const MAX_TICK_DELTA: i32 = 9_116; + +/// Number of entries in each price feed. +/// +/// 6 396 is the maximum that fits within the `DATA_MAX_LENGTH = 100 KiB` runtime ceiling. +/// Each [`ObservationEntry`] is 16 bytes (`timestamp` 8 + `tick_cumulative` 8); fixed overhead +/// is 52 bytes (`price_source_id` 32 + `write_index` 4 + `total_entries` 8 + +/// `last_recorded_tick` 4 + Borsh `Vec` length prefix 4), leaving 102 348 bytes for entries: +/// `floor(102 348 / 16) = 6 396`. +/// +/// The effective history window depends on the `window_duration` used to derive the feed PDA +/// and the sampling guard: `min_interval = window_duration / OBSERVATIONS_CAPACITY`. A 24 h feed +/// samples every ~13 s; a 7 d feed every ~94 s; a 30 d feed every ~7 min. +pub const OBSERVATIONS_CAPACITY: u32 = 6396; + +/// A single price entry written to a [`PriceObservations`]. +#[derive( + Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize, +)] +pub struct ObservationEntry { + /// Block timestamp (milliseconds) when this entry was recorded. + pub timestamp: u64, + /// Running sum of `tick × elapsed_milliseconds` up to this entry. + /// + /// Grows without bound over time, which is why this is `i64` rather than `i32`. + /// The TWAP over any window `[t1, t2]` (timestamps in milliseconds) is computed as + /// `(tick_cumulative[t2] - tick_cumulative[t1]) / (t2 - t1)`. + pub tick_cumulative: i64, +} + +/// Circular price feed of tick observations for a price source and time window. +/// +/// Owned by the TWAP oracle as a PDA derived from +/// `compute_price_observations_pda(oracle_program_id, price_source_id, window_duration)`. +/// The window duration is not stored here — it is implicit in the PDA address. Any caller +/// that locates this account already knows the window duration used to derive it. +/// Only the account that controls `price_source_id` (proven via `is_authorized = true` at call +/// time) may append new entries via `RecordPrice`. +#[account_type] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +pub struct PriceObservations { + /// ID of the price source account this feed is associated with (e.g. an AMM pool). + /// The price feed PDA is derived from this ID and the window duration, so authorization is + /// implicit: whoever controls `price_source_id` is authorized to record prices. + pub price_source_id: AccountId, + /// Index of the *next* slot to write (wraps at `OBSERVATIONS_CAPACITY`). + pub write_index: u32, + /// Total entries ever appended (never resets; used to detect empty/partial-fill state). + pub total_entries: u64, + /// The raw (untruncated) tick from the most recent `RecordTick` call. + /// + /// Used by `RecordTick` to compute the tick delta for the next observation: + /// `delta = current_tick - last_recorded_tick`. Stored as the actual tick, not the + /// clamped value, so that each successive delta is computed from the true price position. + pub last_recorded_tick: i32, + /// Circular price entries; always exactly `OBSERVATIONS_CAPACITY` elements. + pub entries: Vec, +} + +impl TryFrom<&Data> for PriceObservations { + type Error = std::io::Error; + + fn try_from(data: &Data) -> Result { + Self::try_from_slice(data.as_ref()) + } +} + +impl From<&PriceObservations> for Data { + fn from(feed: &PriceObservations) -> Self { + let serialized_len = + borsh::object_length(feed).expect("PriceObservations length must be known"); + let mut data = Vec::with_capacity(serialized_len); + BorshSerialize::serialize(feed, &mut data).expect("Serialization to Vec should not fail"); + Self::try_from(data).expect("PriceObservations encoded data should fit into Data") + } +} + +// ────────────────────────────────────────────────────────────────────────────── +// PDA helpers +// ────────────────────────────────────────────────────────────────────────────── + +const PRICE_OBSERVATIONS_PDA_SEED: [u8; 32] = [2; 32]; + +/// Derives the [`AccountId`] for a price source's [`PriceObservations`] PDA. +/// +/// The `window_duration` is included in the seed so that each `(price_source, window)` pair +/// maps to a distinct account. +#[must_use] +pub fn compute_price_observations_pda( + oracle_program_id: ProgramId, + price_source_id: AccountId, + window_duration: u64, +) -> AccountId { + AccountId::for_public_pda( + &oracle_program_id, + &compute_price_observations_pda_seed(price_source_id, window_duration), + ) +} + +/// Derives the [`PdaSeed`] for a price source's [`PriceObservations`]. +/// +/// Hash input: `price_source_id (32 bytes) || window_duration_le (8 bytes) || +/// PRICE_OBSERVATIONS_PDA_SEED (32 bytes)`. +#[must_use] +pub fn compute_price_observations_pda_seed( + price_source_id: AccountId, + window_duration: u64, +) -> PdaSeed { + use risc0_zkvm::sha::{Impl, Sha256}; + + let mut bytes = [0u8; 72]; + bytes[..32].copy_from_slice(&price_source_id.to_bytes()); + bytes[32..40].copy_from_slice(&window_duration.to_le_bytes()); + bytes[40..72].copy_from_slice(&PRICE_OBSERVATIONS_PDA_SEED); + + PdaSeed::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) +} + +const ORACLE_PRICE_ACCOUNT_PDA_SEED: [u8; 32] = [3; 32]; + +/// Derives the [`AccountId`] for a price source's [`OraclePriceAccount`] PDA. +/// +/// The `window_duration` is included in the seed so that each `(price_source, window)` pair +/// maps to a distinct account, mirroring the [`PriceObservations`] PDA derivation. +#[must_use] +pub fn compute_oracle_price_account_pda( + oracle_program_id: ProgramId, + price_source_id: AccountId, + window_duration: u64, +) -> AccountId { + AccountId::for_public_pda( + &oracle_program_id, + &compute_oracle_price_account_pda_seed(price_source_id, window_duration), + ) +} + +/// Derives the [`PdaSeed`] for a price source's [`OraclePriceAccount`]. +/// +/// Hash input: `price_source_id (32 bytes) || window_duration_le (8 bytes) || +/// ORACLE_PRICE_ACCOUNT_PDA_SEED (32 bytes)`. +#[must_use] +pub fn compute_oracle_price_account_pda_seed( + price_source_id: AccountId, + window_duration: u64, +) -> PdaSeed { + use risc0_zkvm::sha::{Impl, Sha256}; + + let mut bytes = [0u8; 72]; + bytes[..32].copy_from_slice(&price_source_id.to_bytes()); + bytes[32..40].copy_from_slice(&window_duration.to_le_bytes()); + bytes[40..72].copy_from_slice(&ORACLE_PRICE_ACCOUNT_PDA_SEED); + + PdaSeed::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) } /// Canonical oracle price account consumed by LEZ programs. /// -/// Oracle producers own how this account is written; consumers only read and validate it. +/// Oracle producers own how this account is written; consumers only read and +/// validate it. The account shape is intentionally generic so that any oracle +/// type (TWAP, external adaptor, aggregator) can use the same interface. #[account_type] #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct OraclePriceAccount { @@ -27,8 +326,9 @@ pub struct OraclePriceAccount { /// Price observation timestamp. Consumers choose the time unit by matching this with /// `max_age`. pub timestamp: u64, - /// Identifier of the source that populated this account, such as a TWAP or external adaptor. - pub source_id: String, + /// Identifier of the source account that populated this account, such as a TWAP program or + /// external adaptor. + pub source_id: AccountId, /// Source-provided confidence interval, or zero when the source does not provide one. pub confidence_interval: u128, } @@ -51,3 +351,195 @@ impl From<&OraclePriceAccount> for Data { Self::try_from(data).expect("Oracle price account encoded data should fit into Data") } } + +// ────────────────────────────────────────────────────────────────────────────── +// TWAP price encoding +// ────────────────────────────────────────────────────────────────────────────── + +/// Number of fractional bits in the [`OraclePriceAccount::price`] fixed-point value. +/// +/// The price is stored as a `Q64.64` ratio: `OraclePriceAccount::price / 2^PRICE_FRACTIONAL_BITS` +/// is the amount of `quote_asset` one unit of `base_asset` is worth. A consumer multiplies a +/// token amount by the price with `(amount * price) >> PRICE_FRACTIONAL_BITS`. +pub const PRICE_FRACTIONAL_BITS: u32 = 64; + +/// Converts a TWAP tick into the `Q64.64` fixed-point price stored in +/// [`OraclePriceAccount::price`]. +/// +/// The price is `1.0001^tick`, computed via the Uniswap v3 `sqrtPriceX96` representation +/// (pure-integer, no floating point) and then squared back to a plain ratio: +/// +/// ```text +/// sqrtPriceX96 = sqrt(1.0001^tick) * 2^96 +/// price = sqrtPriceX96^2 / 2^128 = 1.0001^tick * 2^64 (Q64.64) +/// ``` +/// +/// `sqrtPriceX96^2` is computed with [`full_math::mul_div`] using a 512-bit intermediate, so it +/// never overflows for any valid tick. The tick is clamped to `[MIN_TICK, MAX_TICK]` and the +/// result saturates at `u128::MAX` for the (practically unreachable) ticks above ~443 636 whose +/// ratio would exceed `2^64`. +/// +/// See `docs/twap-oracle-tick-to-price-conversion.md` for the full derivation. +#[must_use] +pub fn tick_to_oracle_price(tick: i32) -> u128 { + use alloy_primitives::U256; + use uniswap_v3_math::tick_math::{MAX_TICK, MIN_TICK}; + + // 2^128, used to bring sqrtPriceX96^2 (a Q128.128 square) down to Q64.64. + // Built from limbs (little-endian u64 words) to avoid arithmetic operators on U256. + const TWO_POW_128: U256 = U256::from_limbs([0, 0, 1, 0]); + + let clamped_tick = tick.clamp(MIN_TICK, MAX_TICK); + let sqrt_price_x96 = uniswap_v3_math::tick_math::get_sqrt_ratio_at_tick(clamped_tick) + .expect("clamped tick is within [MIN_TICK, MAX_TICK]"); + let price_q64_64 = + uniswap_v3_math::full_math::mul_div(sqrt_price_x96, sqrt_price_x96, TWO_POW_128) + .expect("1.0001^tick * 2^64 fits in U256 for any valid tick"); + + u128::try_from(price_q64_64).unwrap_or(u128::MAX) +} + +// ────────────────────────────────────────────────────────────────────────────── +// Current tick account +// ────────────────────────────────────────────────────────────────────────────── + +/// Live price tick for a price source, written by the price source on every price-changing +/// operation. +/// +/// Owned by the TWAP oracle as a PDA derived from +/// `compute_current_tick_account_pda(oracle_program_id, price_source_id)`. +/// One account exists per price source; it is shared across all time windows for that source. +/// Anyone may call `RecordTick` to advance a [`PriceObservations`] accumulator using the tick +/// stored here. +#[account_type] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)] +pub struct CurrentTickAccount { + /// Most recent raw tick written by the price source: + /// `floor(log_{1.0001}(reserve_b / reserve_a))`. + pub tick: i32, + /// Block timestamp (milliseconds) when `tick` was last written. + pub last_updated: u64, +} + +impl TryFrom<&Data> for CurrentTickAccount { + type Error = std::io::Error; + + fn try_from(data: &Data) -> Result { + Self::try_from_slice(data.as_ref()) + } +} + +impl From<&CurrentTickAccount> for Data { + fn from(account: &CurrentTickAccount) -> Self { + let serialized_len = + borsh::object_length(account).expect("CurrentTickAccount length must be known"); + let mut data = Vec::with_capacity(serialized_len); + BorshSerialize::serialize(account, &mut data) + .expect("Serialization to Vec should not fail"); + Self::try_from(data).expect("CurrentTickAccount encoded data should fit into Data") + } +} + +const CURRENT_TICK_ACCOUNT_PDA_SEED: [u8; 32] = [4; 32]; + +/// Derives the [`AccountId`] for a price source's [`CurrentTickAccount`] PDA. +#[must_use] +pub fn compute_current_tick_account_pda( + oracle_program_id: ProgramId, + price_source_id: AccountId, +) -> AccountId { + AccountId::for_public_pda( + &oracle_program_id, + &compute_current_tick_account_pda_seed(price_source_id), + ) +} + +/// Derives the [`PdaSeed`] for a price source's [`CurrentTickAccount`]. +/// +/// Hash input: `price_source_id (32 bytes) || CURRENT_TICK_ACCOUNT_PDA_SEED (32 bytes)`. +#[must_use] +pub fn compute_current_tick_account_pda_seed(price_source_id: AccountId) -> PdaSeed { + use risc0_zkvm::sha::{Impl, Sha256}; + + let mut bytes = [0u8; 64]; + bytes[..32].copy_from_slice(&price_source_id.to_bytes()); + bytes[32..64].copy_from_slice(&CURRENT_TICK_ACCOUNT_PDA_SEED); + + PdaSeed::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// `1.0` in Q64.64 is `2^64`. + const ONE_Q64_64: u128 = 1u128 << PRICE_FRACTIONAL_BITS; + + #[test] + fn tick_zero_is_unit_price() { + // 1.0001^0 = 1.0 → exactly 2^64 in Q64.64. + assert_eq!(tick_to_oracle_price(0), ONE_Q64_64); + } + + #[test] + fn positive_tick_is_above_unit() { + assert!(tick_to_oracle_price(1) > ONE_Q64_64); + assert!(tick_to_oracle_price(10_000) > ONE_Q64_64); + } + + #[test] + fn negative_tick_is_below_unit() { + assert!(tick_to_oracle_price(-1) < ONE_Q64_64); + assert!(tick_to_oracle_price(-10_000) < ONE_Q64_64); + } + + #[test] + fn price_is_monotonic_in_tick() { + let mut prev = tick_to_oracle_price(-50_000); + for tick in (-49_000..=50_000).step_by(1_000) { + let cur = tick_to_oracle_price(tick); + assert!(cur > prev, "price must increase with tick at {tick}"); + prev = cur; + } + } + + #[test] + fn tick_10000_matches_known_ratio() { + // 1.0001^10000 ≈ 2.71814. Check the ratio in milli-units (× 1000) lands in [2717, 2719] + // using integer math only — `price * 1000 / 2^64` ≈ 2718. + let price = tick_to_oracle_price(10_000); + let ratio_milli = price + .checked_mul(1_000) + .and_then(|scaled| scaled.checked_div(ONE_Q64_64)) + .expect("price * 1000 fits in u128"); + assert!( + (2_717..=2_719).contains(&ratio_milli), + "got {ratio_milli} / 1000" + ); + } + + #[test] + fn extreme_positive_tick_saturates() { + // 1.0001^MAX_TICK far exceeds 2^64, so the Q64.64 value saturates at u128::MAX. + let price = tick_to_oracle_price(uniswap_v3_math::tick_math::MAX_TICK); + assert_eq!(price, u128::MAX); + } + + #[test] + fn ticks_beyond_bounds_are_clamped() { + // Ticks outside [MIN_TICK, MAX_TICK] must not panic; they clamp to the bound. + assert_eq!( + tick_to_oracle_price(i32::MAX), + tick_to_oracle_price(uniswap_v3_math::tick_math::MAX_TICK) + ); + assert_eq!( + tick_to_oracle_price(i32::MIN), + tick_to_oracle_price(uniswap_v3_math::tick_math::MIN_TICK) + ); + } +} diff --git a/programs/twap_oracle/methods/guest/Cargo.lock b/programs/twap_oracle/methods/guest/Cargo.lock index 31ba824..3bbeb85 100644 --- a/programs/twap_oracle/methods/guest/Cargo.lock +++ b/programs/twap_oracle/methods/guest/Cargo.lock @@ -29,6 +29,44 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-primitives" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4885c1409b6936c4898e646ef58baf6ec54edaf6d8179f79df805a7b85b7cf3e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash 0.2.0", + "hashbrown 0.17.1", + "indexmap 2.14.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.4", + "rapidhash", + "ruint", + "rustc-hash", + "secp256k1", + "serde", + "sha3", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -51,9 +89,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-r1cs-std", - "ark-std", + "ark-std 0.5.0", ] [[package]] @@ -65,14 +103,14 @@ dependencies = [ "ahash", "ark-crypto-primitives-macros", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-serialize", + "ark-serialize 0.5.0", "ark-snark", - "ark-std", + "ark-std 0.5.0", "blake2", "derivative", - "digest", + "digest 0.10.7", "fnv", "merlin", "sha2", @@ -96,10 +134,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash", - "ark-ff", + "ark-ff 0.5.0", "ark-poly", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -110,18 +148,56 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "arrayvec", - "digest", + "digest 0.10.7", "educe", "itertools 0.13.0", "num-bigint", @@ -130,6 +206,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.5.0" @@ -140,6 +236,31 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.5.0" @@ -161,11 +282,11 @@ checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" dependencies = [ "ark-crypto-primitives", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-poly", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", ] [[package]] @@ -175,9 +296,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ "ahash", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -190,9 +311,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" dependencies = [ "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-std", + "ark-std 0.5.0", "educe", "num-bigint", "num-integer", @@ -206,12 +327,33 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" dependencies = [ - "ark-ff", - "ark-std", + "ark-ff 0.5.0", + "ark-std 0.5.0", "tracing", "tracing-subscriber", ] +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + [[package]] name = "ark-serialize" version = "0.5.0" @@ -219,9 +361,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ "ark-serialize-derive", - "ark-std", + "ark-std 0.5.0", "arrayvec", - "digest", + "digest 0.10.7", "num-bigint", ] @@ -242,10 +384,30 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" dependencies = [ - "ark-ff", + "ark-ff 0.5.0", "ark-relations", - "ark-serialize", - "ark-std", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.6", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.6", ] [[package]] @@ -276,6 +438,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "autocfg" version = "1.5.1" @@ -315,12 +488,37 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-io" +version = "0.1.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -333,13 +531,25 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -418,6 +628,12 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + [[package]] name = "bytemuck" version = "1.25.0" @@ -488,7 +704,7 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.28", "serde", "serde_json", "thiserror 2.0.18", @@ -550,6 +766,15 @@ dependencies = [ "inout", ] +[[package]] +name = "clock_core" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-execution-zone.git?tag=v0.2.0-rc3#cf3639d8252040d13b3d4e933feb19b42c76e14a" +dependencies = [ + "borsh", + "nssa_core", +] + [[package]] name = "cobs" version = "0.3.0" @@ -559,12 +784,54 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "const-hex" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -610,6 +877,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -788,13 +1061,23 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.117", "unicode-xid", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" @@ -807,6 +1090,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", +] + [[package]] name = "dirs" version = "6.0.0" @@ -825,14 +1118,14 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -875,7 +1168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "serdect", @@ -915,7 +1208,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -982,7 +1275,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", ] [[package]] @@ -991,6 +1294,28 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.1" @@ -1007,6 +1332,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.6", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1019,6 +1356,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1055,6 +1398,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures-channel" version = "0.3.32" @@ -1191,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1199,6 +1548,9 @@ name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", +] [[package]] name = "hashlink" @@ -1221,6 +1573,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -1233,14 +1594,14 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -1286,9 +1647,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" dependencies = [ "atomic-waker", "bytes", @@ -1482,12 +1843,38 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "include_bytes_aligned" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + [[package]] name = "indexmap" version = "1.9.3" @@ -1526,6 +1913,15 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1586,6 +1982,41 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + +[[package]] +name = "keccak-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1766b89733097006f3a1388a02849865d6bc98c89273cb622e29fdd209922183" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy-regex" version = "3.6.0" @@ -1638,9 +2069,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "libc", ] @@ -1691,9 +2122,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "merlin" @@ -1702,7 +2133,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", - "keccak", + "keccak 0.1.6", "rand_core 0.6.4", "zeroize", ] @@ -1860,6 +2291,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "paste" version = "1.0.15" @@ -1881,6 +2340,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1954,13 +2423,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -1990,11 +2470,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ + "bit-set", + "bit-vec", "bitflags 2.11.1", "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", "unarray", ] @@ -2021,6 +2506,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quinn" version = "0.11.9" @@ -2097,12 +2588,19 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ + "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -2164,6 +2662,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rustversion", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -2307,7 +2814,7 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "ruint", - "semver", + "semver 1.0.28", "serde", "tracing", ] @@ -2329,7 +2836,7 @@ dependencies = [ "risc0-zkp", "risc0-zkvm-platform", "rzup", - "semver", + "semver 1.0.28", "serde", "serde_json", "stability", @@ -2404,9 +2911,9 @@ dependencies = [ "anyhow", "ark-bn254", "ark-ec", - "ark-ff", + "ark-ff 0.5.0", "ark-groth16", - "ark-serialize", + "ark-serialize 0.5.0", "bytemuck", "hex", "num-bigint", @@ -2438,7 +2945,7 @@ dependencies = [ "borsh", "bytemuck", "cfg-if", - "digest", + "digest 0.10.7", "hex", "hex-literal", "metal", @@ -2480,7 +2987,7 @@ dependencies = [ "risc0-zkvm-platform", "rrs-lib", "rzup", - "semver", + "semver 1.0.28", "serde", "sha2", "stability", @@ -2504,6 +3011,16 @@ dependencies = [ "stability", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -2521,7 +3038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", - "digest", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -2536,14 +3053,27 @@ dependencies = [ [[package]] name = "ruint" -version = "1.18.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" +checksum = "a68df0380e5c9d20ce49534f292a36a7514ae21350726efe1865bdb1fa91d278" dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", "borsh", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", "proptest", "rand 0.8.6", "rand 0.9.4", + "rlp", "ruint-macro", "serde_core", "valuable", @@ -2562,13 +3092,28 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.28", ] [[package]] @@ -2581,7 +3126,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2625,6 +3170,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.23" @@ -2639,7 +3196,7 @@ checksum = "5d2aed296f203fa64bcb4b52069356dd86d6ec578593985b919b6995bee1f0ae" dependencies = [ "hex", "rsa", - "semver", + "semver 1.0.28", "serde", "serde_with", "sha2", @@ -2689,6 +3246,35 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.4", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.28" @@ -2699,6 +3285,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -2813,7 +3408,27 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", +] + +[[package]] +name = "sha3-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3f15d4e239ebe08413eed880e0f9b5af4b40ee0472543320efa91d488e96a7" +dependencies = [ + "cc", + "cfg-if", ] [[package]] @@ -2828,7 +3443,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -2851,7 +3466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2926,6 +3541,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" @@ -3001,6 +3622,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.27.0" @@ -3011,7 +3638,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3193,9 +3820,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", @@ -3316,6 +3943,7 @@ name = "twap-oracle-guest" version = "0.1.0" dependencies = [ "borsh", + "clock_core", "nssa_core", "risc0-zkvm", "serde", @@ -3328,16 +3956,21 @@ dependencies = [ name = "twap_oracle_core" version = "0.1.0" dependencies = [ + "alloy-primitives", "borsh", "nssa_core", + "risc0-zkvm", + "ruint", "serde", "spel-framework-macros", + "uniswap_v3_math", ] [[package]] name = "twap_oracle_program" version = "0.1.0" dependencies = [ + "clock_core", "nssa_core", "twap_oracle_core", ] @@ -3348,6 +3981,24 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -3360,12 +4011,29 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "uniswap_v3_math" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e393498a831893ce69ed6e1d06615e400bd1e8f97e9fcd113324f2d610fe6d45" +dependencies = [ + "alloy-primitives", + "eyre", + "thiserror 2.0.18", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -3402,6 +4070,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "want" version = "0.3.1" @@ -3534,7 +4211,7 @@ dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap 2.14.0", - "semver", + "semver 1.0.28", ] [[package]] @@ -3885,7 +4562,7 @@ dependencies = [ "id-arena", "indexmap 2.14.0", "log", - "semver", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -3899,6 +4576,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yaml-rust2" version = "0.10.4" @@ -3935,18 +4621,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e" dependencies = [ "proc-macro2", "quote", diff --git a/programs/twap_oracle/methods/guest/Cargo.toml b/programs/twap_oracle/methods/guest/Cargo.toml index 440368d..06f7c97 100644 --- a/programs/twap_oracle/methods/guest/Cargo.toml +++ b/programs/twap_oracle/methods/guest/Cargo.toml @@ -12,6 +12,7 @@ path = "src/bin/twap_oracle.rs" [dependencies] spel-framework = { git = "https://github.com/logos-co/spel.git", tag = "v0.3.0", package = "spel-framework" } nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" } +clock_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" } risc0-zkvm = { version = "=3.0.5", default-features = false } twap_oracle_core = { path = "../../core" } twap_oracle_program = { path = "../..", package = "twap_oracle_program" } diff --git a/programs/twap_oracle/methods/guest/src/bin/twap_oracle.rs b/programs/twap_oracle/methods/guest/src/bin/twap_oracle.rs index 1deb385..4c7075e 100644 --- a/programs/twap_oracle/methods/guest/src/bin/twap_oracle.rs +++ b/programs/twap_oracle/methods/guest/src/bin/twap_oracle.rs @@ -1,5 +1,7 @@ #![cfg_attr(not(test), no_main)] +use nssa_core::account::{AccountId, AccountWithMetadata}; +use spel_framework::context::ProgramContext; use spel_framework::prelude::*; #[cfg(not(test))] @@ -10,9 +12,162 @@ mod twap_oracle { #[allow(unused_imports)] use super::*; - /// No-op instruction. Does nothing and returns no state changes. + /// Creates and initialises a price observations account for a price source and time window. + /// + /// Expected accounts: + /// 1. `price_observations` — uninitialized PDA owned by this oracle program. + /// 2. `price_source` — account the caller controls (proven via `is_authorized = true`); + /// its ID is used as the observations identifier and to derive the price observations PDA. + /// 3. `clock` — read-only LEZ clock account (any cadence). #[instruction] - pub fn noop() -> SpelResult { - Ok(spel_framework::SpelOutput::execute(twap_oracle_program::noop::noop(), vec![])) + pub fn create_price_observations( + ctx: ProgramContext, + price_observations: AccountWithMetadata, + price_source: AccountWithMetadata, + clock: AccountWithMetadata, + initial_tick: i32, + window_duration: u64, + ) -> SpelResult { + let post_states = + twap_oracle_program::create_price_observations::create_price_observations( + price_observations, + price_source, + clock, + initial_tick, + window_duration, + ctx.self_program_id, + ); + Ok(spel_framework::SpelOutput::execute(post_states, vec![])) + } + + /// Creates and initialises a canonical oracle price account for a price source and time + /// window. + /// + /// Expected accounts: + /// 1. `oracle_price_account` — uninitialized PDA owned by this oracle program. + /// 2. `price_source` — account the caller controls (proven via `is_authorized = true`); + /// its ID ties this price account to the same source as the corresponding + /// `PriceObservations` account for the same window. + #[instruction] + pub fn create_oracle_price_account( + ctx: ProgramContext, + oracle_price_account: AccountWithMetadata, + price_source: AccountWithMetadata, + base_asset: AccountId, + quote_asset: AccountId, + window_duration: u64, + ) -> SpelResult { + let post_states = + twap_oracle_program::create_oracle_price_account::create_oracle_price_account( + oracle_price_account, + price_source, + base_asset, + quote_asset, + window_duration, + ctx.self_program_id, + ); + Ok(spel_framework::SpelOutput::execute(post_states, vec![])) + } + + /// Creates and initialises a current tick account for a price source. + /// + /// Expected accounts: + /// 1. `current_tick_account` — uninitialized PDA owned by this oracle program. + /// 2. `price_source` — account the caller controls (proven via `is_authorized = true`). + /// 3. `clock` — read-only LEZ clock account. + #[instruction] + pub fn create_current_tick_account( + ctx: ProgramContext, + current_tick_account: AccountWithMetadata, + price_source: AccountWithMetadata, + clock: AccountWithMetadata, + initial_tick: i32, + ) -> SpelResult { + let post_states = + twap_oracle_program::create_current_tick_account::create_current_tick_account( + current_tick_account, + price_source, + clock, + initial_tick, + ctx.self_program_id, + ); + Ok(spel_framework::SpelOutput::execute(post_states, vec![])) + } + + /// Computes the TWAP from the price observations ring buffer and writes it to the price + /// account. + /// + /// Expected accounts: + /// 1. `price_observations` — initialized PDA owned by this oracle program. + /// 2. `oracle_price_account` — initialized PDA owned by this oracle program. + /// 3. `clock` — read-only LEZ clock account. + #[instruction] + pub fn publish_price( + ctx: ProgramContext, + price_observations: AccountWithMetadata, + oracle_price_account: AccountWithMetadata, + clock: AccountWithMetadata, + price_source_id: AccountId, + window_duration: u64, + ) -> SpelResult { + let post_states = twap_oracle_program::publish_price::publish_price( + price_observations, + oracle_price_account, + clock, + price_source_id, + window_duration, + ctx.self_program_id, + ); + Ok(spel_framework::SpelOutput::execute(post_states, vec![])) + } + + /// Records the current tick into a price observations ring buffer. + /// + /// Expected accounts: + /// 1. `price_observations` — initialized PDA owned by this oracle program. + /// 2. `current_tick_account` — initialized PDA owned by this oracle program. + /// 3. `clock` — read-only LEZ clock account. + #[instruction] + pub fn record_tick( + ctx: ProgramContext, + price_observations: AccountWithMetadata, + current_tick_account: AccountWithMetadata, + clock: AccountWithMetadata, + price_source_id: AccountId, + window_duration: u64, + ) -> SpelResult { + let post_states = twap_oracle_program::record_tick::record_tick( + price_observations, + current_tick_account, + clock, + price_source_id, + window_duration, + ctx.self_program_id, + ); + Ok(spel_framework::SpelOutput::execute(post_states, vec![])) + } + + /// Updates the tick stored in an existing current tick account. + /// + /// Expected accounts: + /// 1. `current_tick_account` — initialized PDA owned by this oracle program. + /// 2. `price_source` — account the caller controls (proven via `is_authorized = true`). + /// 3. `clock` — read-only LEZ clock account. + #[instruction] + pub fn update_current_tick( + ctx: ProgramContext, + current_tick_account: AccountWithMetadata, + price_source: AccountWithMetadata, + clock: AccountWithMetadata, + tick: i32, + ) -> SpelResult { + let post_states = twap_oracle_program::update_current_tick::update_current_tick( + current_tick_account, + price_source, + clock, + tick, + ctx.self_program_id, + ); + Ok(spel_framework::SpelOutput::execute(post_states, vec![])) } } diff --git a/programs/twap_oracle/src/create_current_tick_account.rs b/programs/twap_oracle/src/create_current_tick_account.rs new file mode 100644 index 0000000..38fa73c --- /dev/null +++ b/programs/twap_oracle/src/create_current_tick_account.rs @@ -0,0 +1,280 @@ +use clock_core::ClockAccountData; +use nssa_core::{ + account::{Account, AccountWithMetadata, Data}, + program::{AccountPostState, Claim, ProgramId}, +}; +use twap_oracle_core::{ + compute_current_tick_account_pda, compute_current_tick_account_pda_seed, CurrentTickAccount, +}; + +/// Creates and initialises a [`CurrentTickAccount`] for a price source. +/// +/// Authorization is implicit in the PDA relationship: the current tick account is derived from +/// `price_source.account_id`, so whoever controls the price source controls this account. +/// +/// # Panics +/// Panics if: +/// - `current_tick_account.account_id` does not match +/// `compute_current_tick_account_pda(oracle_program_id, price_source.account_id)`. +/// - `current_tick_account.account` is not the default (already initialised). +/// - `price_source.is_authorized` is false. +pub fn create_current_tick_account( + current_tick_account: AccountWithMetadata, + price_source: AccountWithMetadata, + clock: AccountWithMetadata, + initial_tick: i32, + oracle_program_id: ProgramId, +) -> Vec { + let price_source_id = price_source.account_id; + assert_eq!( + current_tick_account.account_id, + compute_current_tick_account_pda(oracle_program_id, price_source_id), + "CreateCurrentTickAccount: current tick account ID does not match expected PDA" + ); + assert_eq!( + current_tick_account.account, + Account::default(), + "CreateCurrentTickAccount: current tick account must be uninitialized" + ); + assert!( + price_source.is_authorized, + "CreateCurrentTickAccount: price source account must be authorized" + ); + + let clock_data = ClockAccountData::from_bytes(clock.account.data.as_ref()); + + let account = CurrentTickAccount { + tick: initial_tick, + last_updated: clock_data.timestamp, + }; + + let mut current_tick_account_post = current_tick_account.account.clone(); + current_tick_account_post.data = Data::from(&account); + + vec![ + AccountPostState::new_claimed( + current_tick_account_post, + Claim::Pda(compute_current_tick_account_pda_seed(price_source_id)), + ), + AccountPostState::new(price_source.account.clone()), + AccountPostState::new(clock.account.clone()), + ] +} + +#[cfg(test)] +mod tests { + use nssa_core::account::{AccountId, Nonce}; + + use super::*; + + const ORACLE_PROGRAM_ID: ProgramId = [77u32; 8]; + const CLOCK_PROGRAM_ID: ProgramId = [88u32; 8]; + + fn price_source_id() -> AccountId { + AccountId::new([1u8; 32]) + } + + fn clock_account_with_timestamp(timestamp: u64) -> AccountWithMetadata { + let data = ClockAccountData { + block_id: 0, + timestamp, + } + .to_bytes(); + AccountWithMetadata { + account: Account { + program_owner: CLOCK_PROGRAM_ID, + balance: 0, + data: Data::try_from(data).expect("ClockAccountData fits in Data"), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: AccountId::new([99u8; 32]), + } + } + + fn price_source_authorized() -> AccountWithMetadata { + AccountWithMetadata { + account: Account { + program_owner: [42u32; 8], + balance: 0, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: price_source_id(), + } + } + + fn current_tick_account_uninit() -> AccountWithMetadata { + AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + } + } + + // ── happy path ──────────────────────────────────────────────────────────── + + #[test] + fn returns_three_post_states() { + let post_states = create_current_tick_account( + current_tick_account_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + assert_eq!(post_states.len(), 3); + } + + #[test] + fn current_tick_account_post_state_is_pda_claimed() { + let post_states = create_current_tick_account( + current_tick_account_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + assert_eq!( + post_states[0].required_claim(), + Some(Claim::Pda(compute_current_tick_account_pda_seed( + price_source_id() + ))) + ); + } + + #[test] + fn tick_and_timestamp_stored_correctly() { + let timestamp = 123_456_789u64; + let initial_tick = -42i32; + + let post_states = create_current_tick_account( + current_tick_account_uninit(), + price_source_authorized(), + clock_account_with_timestamp(timestamp), + initial_tick, + ORACLE_PROGRAM_ID, + ); + + let account = CurrentTickAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid CurrentTickAccount"); + + assert_eq!(account.tick, initial_tick); + assert_eq!(account.last_updated, timestamp); + } + + #[test] + fn positive_and_negative_initial_ticks_stored_correctly() { + for tick in [i32::MIN, -1, 0, 1, i32::MAX] { + let post_states = create_current_tick_account( + current_tick_account_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + tick, + ORACLE_PROGRAM_ID, + ); + let account = CurrentTickAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid CurrentTickAccount"); + assert_eq!(account.tick, tick); + } + } + + #[test] + fn price_source_and_clock_post_states_are_unchanged() { + let price_source = price_source_authorized(); + let clock = clock_account_with_timestamp(42_000); + + let post_states = create_current_tick_account( + current_tick_account_uninit(), + price_source.clone(), + clock.clone(), + 0, + ORACLE_PROGRAM_ID, + ); + + assert_eq!(*post_states[1].account(), price_source.account); + assert_eq!(*post_states[2].account(), clock.account); + } + + #[test] + fn different_price_sources_produce_distinct_pdas() { + let other_source_id = AccountId::new([2u8; 32]); + assert_ne!( + compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + compute_current_tick_account_pda(ORACLE_PROGRAM_ID, other_source_id), + ); + } + + #[test] + fn current_tick_account_pda_differs_from_price_observations_pda() { + use twap_oracle_core::compute_price_observations_pda; + let window = 24 * 60 * 60 * 1_000u64; + assert_ne!( + compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + compute_price_observations_pda(ORACLE_PROGRAM_ID, price_source_id(), window), + ); + } + + // ── precondition violations ─────────────────────────────────────────────── + + #[test] + #[should_panic(expected = "current tick account ID does not match expected PDA")] + fn wrong_account_id_panics() { + let mut wrong = current_tick_account_uninit(); + wrong.account_id = AccountId::new([0u8; 32]); + create_current_tick_account( + wrong, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "current tick account must be uninitialized")] + fn already_initialized_account_panics() { + let mut initialized = current_tick_account_uninit(); + initialized.account.data = Data::try_from(vec![1u8; 10]).expect("fits in Data"); + create_current_tick_account( + initialized, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price source account must be authorized")] + fn unauthorized_price_source_panics() { + let mut unauthorized = price_source_authorized(); + unauthorized.is_authorized = false; + create_current_tick_account( + current_tick_account_uninit(), + unauthorized, + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } + + /// An attacker who controls their own price source cannot register a current tick account + /// that claims to be derived from a different (victim's) price source. + #[test] + #[should_panic(expected = "current tick account ID does not match expected PDA")] + fn cannot_register_for_another_price_source() { + let victim_source_id = AccountId::new([2u8; 32]); + let victim_pda = compute_current_tick_account_pda(ORACLE_PROGRAM_ID, victim_source_id); + let mut attacker_account = current_tick_account_uninit(); + attacker_account.account_id = victim_pda; + create_current_tick_account( + attacker_account, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } +} diff --git a/programs/twap_oracle/src/create_oracle_price_account.rs b/programs/twap_oracle/src/create_oracle_price_account.rs new file mode 100644 index 0000000..75b4efb --- /dev/null +++ b/programs/twap_oracle/src/create_oracle_price_account.rs @@ -0,0 +1,369 @@ +use nssa_core::{ + account::{Account, AccountId, AccountWithMetadata, Data}, + program::{AccountPostState, Claim, ProgramId}, +}; +use twap_oracle_core::{ + compute_oracle_price_account_pda, compute_oracle_price_account_pda_seed, OraclePriceAccount, +}; + +/// Creates and initialises an [`OraclePriceAccount`] for a price source account and time window. +/// +/// The account is initialised with `price = 0`, `timestamp = 0`, and `confidence_interval = 0`. +/// These are populated later by a `PublishPrice` instruction. Consumers must reject accounts +/// whose `timestamp` is zero or stale. +/// +/// Authorization is implicit in the PDA relationship: the oracle price account is derived from +/// `price_source.account_id` and `window_duration`, so whoever controls the price source +/// controls this account. +/// +/// # Panics +/// Panics if: +/// - `oracle_price_account.account_id` does not match +/// `compute_oracle_price_account_pda(oracle_program_id, price_source.account_id, +/// window_duration)`. +/// - `oracle_price_account.account` is not the default (already initialised). +/// - `price_source.is_authorized` is false (caller does not control the price source account). +pub fn create_oracle_price_account( + oracle_price_account: AccountWithMetadata, + price_source: AccountWithMetadata, + base_asset: AccountId, + quote_asset: AccountId, + window_duration: u64, + oracle_program_id: ProgramId, +) -> Vec { + let price_source_id = price_source.account_id; + assert_eq!( + oracle_price_account.account_id, + compute_oracle_price_account_pda(oracle_program_id, price_source_id, window_duration), + "CreateOraclePriceAccount: oracle price account ID does not match expected PDA" + ); + assert_eq!( + oracle_price_account.account, + Account::default(), + "CreateOraclePriceAccount: oracle price account must be uninitialized" + ); + assert!( + price_source.is_authorized, + "CreateOraclePriceAccount: price source account must be authorized (caller must control it via a PDA)" + ); + + let account = OraclePriceAccount { + base_asset, + quote_asset, + price: 0, + timestamp: 0, + source_id: price_source_id, + confidence_interval: 0, + }; + + let mut oracle_price_account_post = oracle_price_account.account.clone(); + oracle_price_account_post.data = Data::from(&account); + + vec![ + AccountPostState::new_claimed( + oracle_price_account_post, + Claim::Pda(compute_oracle_price_account_pda_seed( + price_source_id, + window_duration, + )), + ), + AccountPostState::new(price_source.account.clone()), + ] +} + +#[cfg(test)] +mod tests { + use nssa_core::account::Nonce; + + use super::*; + + const ORACLE_PROGRAM_ID: ProgramId = [77u32; 8]; + /// 24-hour window in milliseconds. + const WINDOW_24H: u64 = 24 * 60 * 60 * 1_000; + + fn price_source_id() -> AccountId { + AccountId::new([1u8; 32]) + } + + fn base_asset() -> AccountId { + AccountId::new([10u8; 32]) + } + + fn quote_asset() -> AccountId { + AccountId::new([11u8; 32]) + } + + fn price_source_authorized() -> AccountWithMetadata { + AccountWithMetadata { + account: Account { + program_owner: [42u32; 8], + balance: 0, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: price_source_id(), + } + } + + fn oracle_price_account_uninit() -> AccountWithMetadata { + AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_oracle_price_account_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + } + } + + // ── happy path ──────────────────────────────────────────────────────────── + + #[test] + fn returns_two_post_states() { + let post_states = create_oracle_price_account( + oracle_price_account_uninit(), + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(post_states.len(), 2); + } + + #[test] + fn oracle_price_account_post_state_is_pda_claimed() { + let post_states = create_oracle_price_account( + oracle_price_account_uninit(), + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!( + post_states[0].required_claim(), + Some(Claim::Pda(compute_oracle_price_account_pda_seed( + price_source_id(), + WINDOW_24H, + ))) + ); + } + + #[test] + fn price_source_post_state_is_unchanged() { + let price_source = price_source_authorized(); + let post_states = create_oracle_price_account( + oracle_price_account_uninit(), + price_source.clone(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(*post_states[1].account(), price_source.account); + } + + #[test] + fn account_initialised_with_zero_price_and_timestamp() { + let post_states = create_oracle_price_account( + oracle_price_account_uninit(), + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid OraclePriceAccount"); + assert_eq!(account.price, 0); + assert_eq!(account.timestamp, 0); + assert_eq!(account.confidence_interval, 0); + } + + #[test] + fn assets_and_source_id_stored_correctly() { + let post_states = create_oracle_price_account( + oracle_price_account_uninit(), + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid OraclePriceAccount"); + assert_eq!(account.base_asset, base_asset()); + assert_eq!(account.quote_asset, quote_asset()); + assert_eq!(account.source_id, price_source_id()); + } + + /// `source_id` must always equal the price source's `account_id`, regardless of which + /// price source is used. This test uses a distinct source ID to make the invariant explicit. + #[test] + fn source_id_equals_price_source_account_id() { + let other_source_id = AccountId::new([99u8; 32]); + let other_source = AccountWithMetadata { + account: Account { + program_owner: [42u32; 8], + balance: 0, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: other_source_id, + }; + let other_price_account = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_oracle_price_account_pda( + ORACLE_PROGRAM_ID, + other_source_id, + WINDOW_24H, + ), + }; + let post_states = create_oracle_price_account( + other_price_account, + other_source, + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid OraclePriceAccount"); + assert_eq!(account.source_id, other_source_id); + } + + #[test] + fn different_price_sources_produce_distinct_pdas() { + let other_source_id = AccountId::new([2u8; 32]); + assert_ne!( + compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, price_source_id(), WINDOW_24H), + compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, other_source_id, WINDOW_24H), + ); + } + + #[test] + fn different_windows_produce_distinct_pdas() { + let window_7d = 7 * 24 * 60 * 60 * 1_000u64; + assert_ne!( + compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, price_source_id(), WINDOW_24H), + compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, price_source_id(), window_7d), + ); + } + + #[test] + fn oracle_price_account_pda_differs_from_price_observations_pda() { + use twap_oracle_core::compute_price_observations_pda; + assert_ne!( + compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, price_source_id(), WINDOW_24H), + compute_price_observations_pda(ORACLE_PROGRAM_ID, price_source_id(), WINDOW_24H), + ); + } + + /// A plain wallet account (no program owner, no data) can act as the price source just as + /// well as a program-owned PDA. Authorization is conveyed via `is_authorized = true` + /// regardless of account type. + #[test] + fn wallet_account_as_price_source_works() { + let wallet_id = AccountId::new([55u8; 32]); + let wallet = AccountWithMetadata { + account: Account { + program_owner: [0u32; 8], + balance: 1_000, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: wallet_id, + }; + let price_account = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, wallet_id, WINDOW_24H), + }; + let post_states = create_oracle_price_account( + price_account, + wallet, + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid OraclePriceAccount"); + assert_eq!(account.source_id, wallet_id); + assert_eq!(account.base_asset, base_asset()); + assert_eq!(account.quote_asset, quote_asset()); + } + + // ── precondition violations ─────────────────────────────────────────────── + + #[test] + #[should_panic(expected = "oracle price account ID does not match expected PDA")] + fn wrong_oracle_price_account_id_panics() { + let mut wrong = oracle_price_account_uninit(); + wrong.account_id = AccountId::new([0u8; 32]); + create_oracle_price_account( + wrong, + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "oracle price account must be uninitialized")] + fn already_initialized_oracle_price_account_panics() { + let mut initialized = oracle_price_account_uninit(); + initialized.account.data = Data::try_from(vec![1u8; 10]).expect("fits in Data"); + create_oracle_price_account( + initialized, + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price source account must be authorized")] + fn unauthorized_price_source_panics() { + let mut unauthorized = price_source_authorized(); + unauthorized.is_authorized = false; + create_oracle_price_account( + oracle_price_account_uninit(), + unauthorized, + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + /// An attacker who controls their own price source cannot register an oracle price account + /// that claims to be derived from a different (victim's) price source. + #[test] + #[should_panic(expected = "oracle price account ID does not match expected PDA")] + fn cannot_register_price_account_for_another_price_source() { + let victim_source_id = AccountId::new([2u8; 32]); + let victim_pda = + compute_oracle_price_account_pda(ORACLE_PROGRAM_ID, victim_source_id, WINDOW_24H); + let mut attacker_account = oracle_price_account_uninit(); + attacker_account.account_id = victim_pda; + create_oracle_price_account( + attacker_account, + price_source_authorized(), + base_asset(), + quote_asset(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } +} diff --git a/programs/twap_oracle/src/create_price_observations.rs b/programs/twap_oracle/src/create_price_observations.rs new file mode 100644 index 0000000..9c94226 --- /dev/null +++ b/programs/twap_oracle/src/create_price_observations.rs @@ -0,0 +1,388 @@ +use clock_core::ClockAccountData; +use nssa_core::{ + account::{Account, AccountWithMetadata, Data}, + program::{AccountPostState, Claim, ProgramId}, +}; +use twap_oracle_core::{ + compute_price_observations_pda, compute_price_observations_pda_seed, ObservationEntry, + PriceObservations, OBSERVATIONS_CAPACITY, +}; + +/// Creates and initialises a [`PriceObservations`] for a price source account and time window. +/// +/// Authorization is implicit in the PDA relationship: the price observations account is derived +/// from `price_source.account_id` and `window_duration`, so whoever controls the price source +/// controls the observations account. +/// +/// # Panics +/// Panics if: +/// - `price_observations.account_id` does not match +/// `compute_price_observations_pda(oracle_program_id, price_source.account_id, window_duration)`. +/// - `price_observations.account` is not the default (already initialised). +/// - `price_source.is_authorized` is false (caller does not control the price source account). +pub fn create_price_observations( + price_observations: AccountWithMetadata, + price_source: AccountWithMetadata, + clock: AccountWithMetadata, + initial_tick: i32, + window_duration: u64, + oracle_program_id: ProgramId, +) -> Vec { + let price_source_id = price_source.account_id; + assert_eq!( + price_observations.account_id, + compute_price_observations_pda(oracle_program_id, price_source_id, window_duration), + "CreatePriceObservations: price observations account ID does not match expected PDA" + ); + assert_eq!( + price_observations.account, + Account::default(), + "CreatePriceObservations: price observations account must be uninitialized" + ); + assert!( + price_source.is_authorized, + "CreatePriceObservations: price source account must be authorized (caller must control it via a PDA)" + ); + + let clock_data = ClockAccountData::from_bytes(clock.account.data.as_ref()); + + let capacity = + usize::try_from(OBSERVATIONS_CAPACITY).expect("OBSERVATIONS_CAPACITY fits in usize"); + let mut entries = vec![ObservationEntry::default(); capacity]; + *entries + .first_mut() + .expect("OBSERVATIONS_CAPACITY is non-zero") = ObservationEntry { + timestamp: clock_data.timestamp, + tick_cumulative: 0, + }; + + let observations = PriceObservations { + price_source_id, + write_index: 1, + total_entries: 1, + last_recorded_tick: initial_tick, + entries, + }; + + let mut price_observations_post = price_observations.account.clone(); + price_observations_post.data = Data::from(&observations); + + vec![ + AccountPostState::new_claimed( + price_observations_post, + Claim::Pda(compute_price_observations_pda_seed( + price_source_id, + window_duration, + )), + ), + AccountPostState::new(price_source.account.clone()), + AccountPostState::new(clock.account.clone()), + ] +} + +#[cfg(test)] +mod tests { + use nssa_core::account::{AccountId, Nonce}; + + use super::*; + + const ORACLE_PROGRAM_ID: ProgramId = [77u32; 8]; + const CLOCK_PROGRAM_ID: ProgramId = [88u32; 8]; + /// 24-hour window in milliseconds, used as the default window for tests. + const WINDOW_24H: u64 = 24 * 60 * 60 * 1_000; + + fn price_source_id() -> AccountId { + AccountId::new([1u8; 32]) + } + + fn clock_account_with_timestamp(timestamp: u64) -> AccountWithMetadata { + let data = ClockAccountData { + block_id: 0, + timestamp, + } + .to_bytes(); + AccountWithMetadata { + account: Account { + program_owner: CLOCK_PROGRAM_ID, + balance: 0, + data: Data::try_from(data).expect("ClockAccountData fits in Data"), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: AccountId::new([99u8; 32]), + } + } + + fn price_source_authorized() -> AccountWithMetadata { + AccountWithMetadata { + account: Account { + program_owner: [42u32; 8], + balance: 0, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: price_source_id(), + } + } + + fn price_observations_uninit() -> AccountWithMetadata { + AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_price_observations_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + } + } + + // ── happy path ──────────────────────────────────────────────────────────── + + #[test] + fn returns_three_post_states() { + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(post_states.len(), 3); + } + + #[test] + fn price_observations_post_state_is_pda_claimed() { + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!( + post_states[0].required_claim(), + Some(Claim::Pda(compute_price_observations_pda_seed( + price_source_id(), + WINDOW_24H + ))) + ); + } + + #[test] + fn price_source_and_clock_post_states_are_unchanged() { + let price_source = price_source_authorized(); + let clock = clock_account_with_timestamp(42_000); + let post_states = create_price_observations( + price_observations_uninit(), + price_source.clone(), + clock.clone(), + 10, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(*post_states[1].account(), price_source.account); + assert_eq!(*post_states[2].account(), clock.account); + } + + #[test] + fn initial_observation_has_zero_cumulative_and_correct_timestamp() { + let timestamp = 123_456_789u64; + + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(timestamp), + -42, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + + let feed = PriceObservations::try_from(&post_states[0].account().data) + .expect("post state must contain a valid PriceObservations"); + + assert_eq!(feed.entries[0].tick_cumulative, 0); + assert_eq!(feed.entries[0].timestamp, timestamp); + } + + #[test] + fn initial_tick_stored_as_last_recorded_tick() { + let initial_tick = -42i32; + + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + initial_tick, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + + let feed = PriceObservations::try_from(&post_states[0].account().data) + .expect("post state must contain a valid PriceObservations"); + + assert_eq!(feed.last_recorded_tick, initial_tick); + } + + #[test] + fn write_index_and_total_entries_start_at_one() { + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + + let feed = PriceObservations::try_from(&post_states[0].account().data) + .expect("post state must contain a valid PriceObservations"); + + assert_eq!(feed.write_index, 1); + assert_eq!(feed.total_entries, 1); + } + + #[test] + fn remaining_entries_are_default() { + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + + let feed = PriceObservations::try_from(&post_states[0].account().data) + .expect("post state must contain a valid PriceObservations"); + + assert_eq!( + feed.entries.len(), + usize::try_from(OBSERVATIONS_CAPACITY).expect("OBSERVATIONS_CAPACITY fits in usize") + ); + assert!(feed.entries[1..] + .iter() + .all(|e| *e == ObservationEntry::default())); + } + + #[test] + fn price_source_id_stored_correctly() { + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + + let feed = PriceObservations::try_from(&post_states[0].account().data) + .expect("post state must contain a valid PriceObservations"); + + assert_eq!(feed.price_source_id, price_source_id()); + } + + #[test] + fn different_windows_produce_distinct_pdas() { + let window_24h = 24 * 60 * 60 * 1_000u64; + let window_7d = 7 * 24 * 60 * 60 * 1_000u64; + assert_ne!( + compute_price_observations_pda(ORACLE_PROGRAM_ID, price_source_id(), window_24h), + compute_price_observations_pda(ORACLE_PROGRAM_ID, price_source_id(), window_7d), + ); + } + + #[test] + fn positive_and_negative_initial_ticks_stored_as_last_recorded_tick() { + for tick in [i32::MIN, -1, 0, 1, i32::MAX] { + let post_states = create_price_observations( + price_observations_uninit(), + price_source_authorized(), + clock_account_with_timestamp(0), + tick, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let feed = PriceObservations::try_from(&post_states[0].account().data) + .expect("post state must contain a valid PriceObservations"); + assert_eq!(feed.last_recorded_tick, tick); + } + } + + // ── precondition violations ─────────────────────────────────────────────── + + #[test] + #[should_panic(expected = "price observations account ID does not match expected PDA")] + fn wrong_price_feed_account_id_panics() { + let mut wrong_feed = price_observations_uninit(); + wrong_feed.account_id = AccountId::new([0u8; 32]); + create_price_observations( + wrong_feed, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + /// An attacker who controls their own price source cannot register an observations account + /// that claims to be derived from a *different* (victim's) price source. + /// + /// The PDA derivation ties the observations account to the price source that was passed: if + /// the caller supplies their own authorized source but the victim's observations account ID, + /// the PDA check will fail because the computed PDA (from attacker's source) won't match. + #[test] + #[should_panic(expected = "price observations account ID does not match expected PDA")] + fn cannot_register_observations_for_another_price_source() { + let victim_source_id = AccountId::new([2u8; 32]); + // The attacker passes the victim's observations PDA as the target account… + let victim_observations_pda = + compute_price_observations_pda(ORACLE_PROGRAM_ID, victim_source_id, WINDOW_24H); + let mut attacker_observations = price_observations_uninit(); + attacker_observations.account_id = victim_observations_pda; + + // …but only controls their own price source (price_source_id = [1u8; 32]). + create_price_observations( + attacker_observations, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price observations account must be uninitialized")] + fn already_initialized_price_feed_panics() { + let mut initialized_feed = price_observations_uninit(); + initialized_feed.account.data = Data::try_from(vec![1u8; 10]).expect("fits in Data"); + create_price_observations( + initialized_feed, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price source account must be authorized")] + fn unauthorized_price_source_panics() { + let mut unauthorized = price_source_authorized(); + unauthorized.is_authorized = false; + create_price_observations( + price_observations_uninit(), + unauthorized, + clock_account_with_timestamp(0), + 0, + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } +} diff --git a/programs/twap_oracle/src/lib.rs b/programs/twap_oracle/src/lib.rs index 221b2f9..9f7e825 100644 --- a/programs/twap_oracle/src/lib.rs +++ b/programs/twap_oracle/src/lib.rs @@ -2,4 +2,9 @@ pub use twap_oracle_core as core; -pub mod noop; +pub mod create_current_tick_account; +pub mod create_oracle_price_account; +pub mod create_price_observations; +pub mod publish_price; +pub mod record_tick; +pub mod update_current_tick; diff --git a/programs/twap_oracle/src/noop.rs b/programs/twap_oracle/src/noop.rs deleted file mode 100644 index a2eadea..0000000 --- a/programs/twap_oracle/src/noop.rs +++ /dev/null @@ -1,5 +0,0 @@ -use nssa_core::program::AccountPostState; - -pub fn noop() -> Vec { - vec![] -} diff --git a/programs/twap_oracle/src/publish_price.rs b/programs/twap_oracle/src/publish_price.rs new file mode 100644 index 0000000..504dbfe --- /dev/null +++ b/programs/twap_oracle/src/publish_price.rs @@ -0,0 +1,510 @@ +use clock_core::ClockAccountData; +use nssa_core::{ + account::{AccountId, AccountWithMetadata, Data}, + program::{AccountPostState, ProgramId}, +}; +use twap_oracle_core::{ + compute_oracle_price_account_pda, compute_price_observations_pda, tick_to_oracle_price, + OraclePriceAccount, PriceObservations, OBSERVATIONS_CAPACITY, +}; + +/// Computes the TWAP over the full span of the [`PriceObservations`] ring buffer and writes +/// the result to the [`OraclePriceAccount`]. +/// +/// Each observations account is calibrated to a specific `window_duration` via its sampling +/// guard (`min_interval = window_duration / OBSERVATIONS_CAPACITY`), so the oldest valid entry +/// is always the natural start of the window — no boundary search is needed. +/// +/// Returns all accounts unchanged when fewer than two observations are available. The price +/// account stays at `timestamp = 0` (uninitialized signal) until there is something to publish. +/// +/// # Panics +/// Panics if: +/// - `price_observations.account_id` does not match +/// `compute_price_observations_pda(oracle_program_id, price_source_id, window_duration)`. +/// - `oracle_price_account.account_id` does not match +/// `compute_oracle_price_account_pda(oracle_program_id, price_source_id, window_duration)`. +/// - Either account is not a valid initialised account of its respective type. +pub fn publish_price( + price_observations: AccountWithMetadata, + oracle_price_account: AccountWithMetadata, + clock: AccountWithMetadata, + price_source_id: AccountId, + window_duration: u64, + oracle_program_id: ProgramId, +) -> Vec { + assert_eq!( + price_observations.account_id, + compute_price_observations_pda(oracle_program_id, price_source_id, window_duration), + "PublishPrice: price observations account ID does not match expected PDA" + ); + assert_eq!( + oracle_price_account.account_id, + compute_oracle_price_account_pda(oracle_program_id, price_source_id, window_duration), + "PublishPrice: oracle price account ID does not match expected PDA" + ); + + let clock_data = ClockAccountData::from_bytes(clock.account.data.as_ref()); + let now = clock_data.timestamp; + + let observations = PriceObservations::try_from(&price_observations.account.data) + .expect("PublishPrice: price observations account must be initialized"); + let mut price_account = OraclePriceAccount::try_from(&oracle_price_account.account.data) + .expect("PublishPrice: oracle price account must be initialized"); + + // No-op: need at least two observations to compute a TWAP. + if observations.total_entries < 2 { + return vec![ + AccountPostState::new(price_observations.account.clone()), + AccountPostState::new(oracle_price_account.account.clone()), + AccountPostState::new(clock.account.clone()), + ]; + } + + let capacity = + usize::try_from(OBSERVATIONS_CAPACITY).expect("OBSERVATIONS_CAPACITY fits in usize"); + + // t2: the most recent observation. + let t2_index = if observations.write_index == 0 { + capacity + .checked_sub(1) + .expect("OBSERVATIONS_CAPACITY is non-zero") + } else { + usize::try_from( + observations + .write_index + .checked_sub(1) + .expect("write_index > 0"), + ) + .expect("write_index - 1 fits in usize") + }; + + // t1: the oldest valid observation. Once the buffer is full, the oldest entry sits at + // write_index (the slot about to be overwritten next). Before that, entries start at 0. + let is_full = observations.total_entries >= u64::from(OBSERVATIONS_CAPACITY); + let t1_index = if is_full { + usize::try_from(observations.write_index).expect("write_index fits in usize") + } else { + 0 + }; + + let t1 = observations + .entries + .get(t1_index) + .expect("t1_index is within bounds"); + let t2 = observations + .entries + .get(t2_index) + .expect("t2_index is within bounds"); + + let elapsed_ms = t2 + .timestamp + .checked_sub(t1.timestamp) + .expect("t2.timestamp >= t1.timestamp"); + let cumulative_diff = t2 + .tick_cumulative + .checked_sub(t1.tick_cumulative) + .expect("tick_cumulative difference fits in i64"); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let twap_tick_i64 = cumulative_diff + .checked_div(elapsed_ms_i64) + .expect("elapsed_ms is non-zero"); + let twap_tick = i32::try_from(twap_tick_i64).expect("TWAP tick fits in i32"); + + price_account.price = tick_to_oracle_price(twap_tick); + price_account.timestamp = now; + + let mut oracle_price_account_post = oracle_price_account.account.clone(); + oracle_price_account_post.data = Data::from(&price_account); + + vec![ + AccountPostState::new(price_observations.account.clone()), + AccountPostState::new(oracle_price_account_post), + AccountPostState::new(clock.account.clone()), + ] +} + +#[cfg(test)] +mod tests { + use nssa_core::account::{Account, AccountId, Nonce}; + use twap_oracle_core::{ + compute_oracle_price_account_pda, compute_price_observations_pda, tick_to_oracle_price, + ObservationEntry, OraclePriceAccount, PriceObservations, OBSERVATIONS_CAPACITY, + }; + + use super::*; + + const ORACLE_PROGRAM_ID: ProgramId = [77u32; 8]; + const CLOCK_PROGRAM_ID: ProgramId = [88u32; 8]; + const WINDOW_24H: u64 = 24 * 60 * 60 * 1_000; + + fn price_source_id() -> AccountId { + AccountId::new([1u8; 32]) + } + fn base_asset_id() -> AccountId { + AccountId::new([10u8; 32]) + } + fn quote_asset_id() -> AccountId { + AccountId::new([11u8; 32]) + } + + fn clock_account_with_timestamp(timestamp: u64) -> AccountWithMetadata { + let data = ClockAccountData { + block_id: 0, + timestamp, + } + .to_bytes(); + AccountWithMetadata { + account: Account { + program_owner: CLOCK_PROGRAM_ID, + balance: 0, + data: Data::try_from(data).expect("ClockAccountData fits in Data"), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: AccountId::new([99u8; 32]), + } + } + + /// Builds a [`PriceObservations`] from `(timestamp_ms, tick_cumulative)` pairs written in + /// order starting at index 0. `write_index` is set to `entries_data.len()`. + fn make_price_observations( + entries_data: &[(u64, i64)], + last_recorded_tick: i32, + ) -> AccountWithMetadata { + let capacity = + usize::try_from(OBSERVATIONS_CAPACITY).expect("OBSERVATIONS_CAPACITY fits in usize"); + let mut entries = vec![ObservationEntry::default(); capacity]; + for (i, &(timestamp, tick_cumulative)) in entries_data.iter().enumerate() { + *entries.get_mut(i).expect("i < capacity") = ObservationEntry { + timestamp, + tick_cumulative, + }; + } + let write_index = u32::try_from(entries_data.len()).expect("entry count fits in u32"); + let total_entries = u64::try_from(entries_data.len()).expect("entry count fits in u64"); + let obs = PriceObservations { + price_source_id: price_source_id(), + write_index, + total_entries, + last_recorded_tick, + entries, + }; + AccountWithMetadata { + account: Account { + program_owner: ORACLE_PROGRAM_ID, + balance: 0, + data: Data::from(&obs), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: compute_price_observations_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + } + } + + fn make_oracle_price_account() -> AccountWithMetadata { + let account = OraclePriceAccount { + base_asset: base_asset_id(), + quote_asset: quote_asset_id(), + price: 0, + timestamp: 0, + source_id: price_source_id(), + confidence_interval: 0, + }; + AccountWithMetadata { + account: Account { + program_owner: ORACLE_PROGRAM_ID, + balance: 0, + data: Data::from(&account), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: compute_oracle_price_account_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + } + } + + // ── happy path ──────────────────────────────────────────────────────────── + + #[test] + fn returns_three_post_states() { + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(post_states.len(), 3); + } + + #[test] + fn twap_tick_computed_and_stored_as_price() { + // Constant tick = 100 over 24 h → twap = 100 + let cumulative = 100_i64 + .checked_mul(i64::try_from(WINDOW_24H).expect("fits")) + .expect("100 * WINDOW_24H fits in i64"); + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, cumulative)], 100), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + assert_eq!(account.price, tick_to_oracle_price(100)); + } + + #[test] + fn negative_twap_tick_stored_correctly() { + // Constant tick = -50 over 24 h → twap = -50 + let cumulative = (-50_i64) + .checked_mul(i64::try_from(WINDOW_24H).expect("fits")) + .expect("-50 * WINDOW_24H fits in i64"); + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, cumulative)], -50), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + assert_eq!(account.price, tick_to_oracle_price(-50)); + } + + #[test] + fn zero_twap_tick_stored_correctly() { + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + assert_eq!(account.price, tick_to_oracle_price(0)); + } + + #[test] + fn timestamp_set_to_clock_now() { + let now = WINDOW_24H + .checked_mul(2) + .expect("WINDOW_24H * 2 fits in u64"); + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + make_oracle_price_account(), + clock_account_with_timestamp(now), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + assert_eq!(account.timestamp, now); + } + + #[test] + fn other_price_account_fields_preserved() { + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + assert_eq!(account.base_asset, base_asset_id()); + assert_eq!(account.quote_asset, quote_asset_id()); + assert_eq!(account.source_id, price_source_id()); + assert_eq!(account.confidence_interval, 0); + } + + #[test] + fn price_observations_account_is_not_modified() { + let observations = make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0); + let post_states = publish_price( + observations.clone(), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(*post_states[0].account(), observations.account); + } + + #[test] + fn clock_account_is_not_modified() { + let clock = clock_account_with_timestamp(WINDOW_24H); + let post_states = publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + make_oracle_price_account(), + clock.clone(), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(*post_states[2].account(), clock.account); + } + + #[test] + fn twap_uses_oldest_and_newest_entries() { + // Three observations: tick 0 for first half, tick 200 for second half. + // t1 = entry[0] (oldest), t2 = entry[2] (newest). + // Average over full span = (0 * half + 200 * half) / full = 100. + let half = WINDOW_24H.checked_div(2).expect("fits"); + let half_i64 = i64::try_from(half).expect("fits"); + let full_i64 = i64::try_from(WINDOW_24H).expect("fits"); + // entry[0]: t=0, cumulative=0 (tick was 0 before this) + // entry[1]: t=half, cumulative=0 (tick=0 held from 0..half, so 0*half=0) + // entry[2]: t=WINDOW_24H, cumulative=200*half (tick=200 held from half..full) + let cumulative_at_half = 0_i64; + let cumulative_at_full = 200_i64.checked_mul(half_i64).expect("fits"); + let post_states = publish_price( + make_price_observations( + &[ + (0, 0), + (half, cumulative_at_half), + (WINDOW_24H, cumulative_at_full), + ], + 200, + ), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + // twap = (cumulative_at_full - 0) / (WINDOW_24H - 0) = 200*half / full = 200/2 = 100 + let expected_tick = cumulative_at_full.checked_div(full_i64).expect("non-zero"); + assert_eq!( + account.price, + tick_to_oracle_price(i32::try_from(expected_tick).expect("tick fits in i32")) + ); + } + + // ── no-op: insufficient history ─────────────────────────────────────────── + + #[test] + fn noop_when_only_one_observation() { + let initial = make_oracle_price_account(); + let post_states = publish_price( + make_price_observations(&[(0, 0)], 0), + initial.clone(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(*post_states[1].account(), initial.account); + } + + #[test] + fn noop_leaves_price_account_timestamp_at_zero() { + let post_states = publish_price( + make_price_observations(&[(0, 0)], 0), + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let account = OraclePriceAccount::try_from(&post_states[1].account().data) + .expect("valid OraclePriceAccount"); + assert_eq!(account.timestamp, 0); + } + + // ── precondition violations ─────────────────────────────────────────────── + + #[test] + #[should_panic(expected = "price observations account ID does not match expected PDA")] + fn wrong_price_observations_id_panics() { + let mut wrong = make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0); + wrong.account_id = AccountId::new([0u8; 32]); + publish_price( + wrong, + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "oracle price account ID does not match expected PDA")] + fn wrong_oracle_price_account_id_panics() { + let mut wrong = make_oracle_price_account(); + wrong.account_id = AccountId::new([0u8; 32]); + publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + wrong, + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price observations account must be initialized")] + fn uninitialized_price_observations_panics() { + let uninit = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_price_observations_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + }; + publish_price( + uninit, + make_oracle_price_account(), + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "oracle price account must be initialized")] + fn uninitialized_oracle_price_account_panics() { + let uninit = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_oracle_price_account_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + }; + publish_price( + make_price_observations(&[(0, 0), (WINDOW_24H, 0)], 0), + uninit, + clock_account_with_timestamp(WINDOW_24H), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } +} diff --git a/programs/twap_oracle/src/record_tick.rs b/programs/twap_oracle/src/record_tick.rs new file mode 100644 index 0000000..51a2730 --- /dev/null +++ b/programs/twap_oracle/src/record_tick.rs @@ -0,0 +1,710 @@ +use clock_core::ClockAccountData; +use nssa_core::{ + account::{AccountId, AccountWithMetadata, Data}, + program::{AccountPostState, ProgramId}, +}; +use twap_oracle_core::{ + compute_current_tick_account_pda, compute_price_observations_pda, CurrentTickAccount, + ObservationEntry, PriceObservations, MAX_TICK_DELTA, OBSERVATIONS_CAPACITY, +}; + +/// Records the current tick from a [`CurrentTickAccount`] into a [`PriceObservations`] ring +/// buffer. +/// +/// Both PDAs are verified against `price_source_id`, ensuring the tick was written by whoever +/// controls that price source. The sampling guard silently returns all accounts unchanged when +/// less than `window_duration / OBSERVATIONS_CAPACITY` milliseconds have elapsed since the last +/// observation — callers may invoke this on every block without correctness concerns. +/// +/// Tick-delta truncation clamps the per-observation price move to [`MAX_TICK_DELTA`] before +/// advancing the accumulator. `last_recorded_tick` is updated to the raw (untruncated) tick so +/// the next delta is computed from the true price position. +/// +/// # Panics +/// Panics if: +/// - `current_tick_account.account_id` does not match +/// `compute_current_tick_account_pda(oracle_program_id, price_source_id)`. +/// - `price_observations.account_id` does not match +/// `compute_price_observations_pda(oracle_program_id, price_source_id, window_duration)`. +/// - Either account is not a valid initialised account of its respective type. +pub fn record_tick( + price_observations: AccountWithMetadata, + current_tick_account: AccountWithMetadata, + clock: AccountWithMetadata, + price_source_id: AccountId, + window_duration: u64, + oracle_program_id: ProgramId, +) -> Vec { + assert_eq!( + current_tick_account.account_id, + compute_current_tick_account_pda(oracle_program_id, price_source_id), + "RecordTick: current tick account ID does not match expected PDA" + ); + assert_eq!( + price_observations.account_id, + compute_price_observations_pda(oracle_program_id, price_source_id, window_duration), + "RecordTick: price observations account ID does not match expected PDA" + ); + + let clock_data = ClockAccountData::from_bytes(clock.account.data.as_ref()); + let now = clock_data.timestamp; + + let current_tick_data = CurrentTickAccount::try_from(¤t_tick_account.account.data) + .expect("RecordTick: current tick account must be initialized"); + + let mut observations = PriceObservations::try_from(&price_observations.account.data) + .expect("RecordTick: price observations account must be initialized"); + + let capacity = + usize::try_from(OBSERVATIONS_CAPACITY).expect("OBSERVATIONS_CAPACITY fits in usize"); + + // Sampling guard: enforce minimum interval between observations. + let min_interval = window_duration + .checked_div(u64::from(OBSERVATIONS_CAPACITY)) + .expect("OBSERVATIONS_CAPACITY is non-zero"); + let last_index = if observations.write_index == 0 { + capacity + .checked_sub(1) + .expect("OBSERVATIONS_CAPACITY is non-zero") + } else { + usize::try_from( + observations + .write_index + .checked_sub(1) + .expect("write_index > 0"), + ) + .expect("write_index - 1 fits in usize") + }; + let last_entry = observations + .entries + .get(last_index) + .expect("last_index is within bounds"); + let last_timestamp = last_entry.timestamp; + let last_cumulative = last_entry.tick_cumulative; + let elapsed_ms = now.saturating_sub(last_timestamp); + + if elapsed_ms < min_interval { + return vec![ + AccountPostState::new(price_observations.account.clone()), + AccountPostState::new(current_tick_account.account.clone()), + AccountPostState::new(clock.account.clone()), + ]; + } + + // Tick-delta truncation. + let current_tick = current_tick_data.tick; + let delta = current_tick.saturating_sub(observations.last_recorded_tick); + let clamped_delta = delta.clamp(-MAX_TICK_DELTA, MAX_TICK_DELTA); + + // Advance cumulative (tick × elapsed milliseconds). + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let new_cumulative = i64::from(clamped_delta) + .checked_mul(elapsed_ms_i64) + .and_then(|product| last_cumulative.checked_add(product)) + .expect("tick_cumulative fits in i64"); + + // Write new entry and advance the ring buffer. + let write_index = usize::try_from(observations.write_index).expect("write_index fits in usize"); + *observations + .entries + .get_mut(write_index) + .expect("write_index is within bounds") = ObservationEntry { + timestamp: now, + tick_cumulative: new_cumulative, + }; + let next_index = write_index + .checked_add(1) + .expect("write_index + 1 fits in usize") + .checked_rem(capacity) + .expect("capacity is non-zero"); + observations.write_index = u32::try_from(next_index).expect("next write_index fits in u32"); + observations.total_entries = observations + .total_entries + .checked_add(1) + .expect("total_entries does not overflow"); + observations.last_recorded_tick = current_tick; + + let mut price_observations_post = price_observations.account.clone(); + price_observations_post.data = Data::from(&observations); + + vec![ + AccountPostState::new(price_observations_post), + AccountPostState::new(current_tick_account.account.clone()), + AccountPostState::new(clock.account.clone()), + ] +} + +#[cfg(test)] +mod tests { + use nssa_core::account::{Account, AccountId, Nonce}; + use twap_oracle_core::{ + compute_current_tick_account_pda, compute_price_observations_pda, OBSERVATIONS_CAPACITY, + }; + + use super::*; + + const ORACLE_PROGRAM_ID: ProgramId = [77u32; 8]; + const CLOCK_PROGRAM_ID: ProgramId = [88u32; 8]; + const WINDOW_24H: u64 = 24 * 60 * 60 * 1_000; + + fn price_source_id() -> AccountId { + AccountId::new([1u8; 32]) + } + + /// Minimum interval enforced by the sampling guard for `WINDOW_24H`. + fn min_interval() -> u64 { + WINDOW_24H + .checked_div(u64::from(OBSERVATIONS_CAPACITY)) + .expect("OBSERVATIONS_CAPACITY is non-zero") + } + + fn clock_account_with_timestamp(timestamp: u64) -> AccountWithMetadata { + let data = ClockAccountData { + block_id: 0, + timestamp, + } + .to_bytes(); + AccountWithMetadata { + account: Account { + program_owner: CLOCK_PROGRAM_ID, + balance: 0, + data: Data::try_from(data).expect("ClockAccountData fits in Data"), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: AccountId::new([99u8; 32]), + } + } + + fn make_current_tick_account(tick: i32, last_updated: u64) -> AccountWithMetadata { + let stored = CurrentTickAccount { tick, last_updated }; + AccountWithMetadata { + account: Account { + program_owner: ORACLE_PROGRAM_ID, + balance: 0, + data: Data::from(&stored), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + } + } + + /// Builds a [`PriceObservations`] placing a seeded entry at the slot just before + /// `write_index` so `record_tick` reads it as the last observation. + fn make_price_observations( + write_index: u32, + last_recorded_tick: i32, + last_timestamp: u64, + last_cumulative: i64, + ) -> AccountWithMetadata { + let capacity = + usize::try_from(OBSERVATIONS_CAPACITY).expect("OBSERVATIONS_CAPACITY fits in usize"); + let last_index = if write_index == 0 { + capacity + .checked_sub(1) + .expect("OBSERVATIONS_CAPACITY is non-zero") + } else { + usize::try_from(write_index.checked_sub(1).expect("write_index > 0")) + .expect("write_index - 1 fits in usize") + }; + let mut entries = vec![ObservationEntry::default(); capacity]; + *entries + .get_mut(last_index) + .expect("last_index is within bounds") = ObservationEntry { + timestamp: last_timestamp, + tick_cumulative: last_cumulative, + }; + let obs = PriceObservations { + price_source_id: price_source_id(), + write_index, + total_entries: 1, + last_recorded_tick, + entries, + }; + AccountWithMetadata { + account: Account { + program_owner: ORACLE_PROGRAM_ID, + balance: 0, + data: Data::from(&obs), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: compute_price_observations_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + } + } + + // ── happy path ──────────────────────────────────────────────────────────── + + #[test] + fn returns_three_post_states() { + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(post_states.len(), 3); + } + + #[test] + fn cumulative_advances_correctly() { + // last_recorded_tick = 0, current_tick = 100, elapsed = 10_000 ms + // expected: 0 + 100 * 10_000 = 1_000_000 + let elapsed_ms = 10_000u64.max(min_interval()); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(100, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = 100_i64 + .checked_mul(elapsed_ms_i64) + .expect("100 * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + #[test] + fn cumulative_advances_from_non_zero_base() { + // last_cumulative = 500_000, current_tick = 50, elapsed = 20_000 ms + // expected: 500_000 + 50 * 20_000 = 1_500_000 + let elapsed_ms = 20_000u64.max(min_interval()); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 500_000), + make_current_tick_account(50, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = 500_000_i64 + .checked_add( + 50_i64 + .checked_mul(elapsed_ms_i64) + .expect("50 * elapsed_ms fits in i64"), + ) + .expect("500_000 + 50 * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + #[test] + fn negative_tick_decrements_cumulative() { + // last_recorded_tick = 0, current_tick = -100, elapsed = 10_000 ms + // expected: 0 + (-100) * 10_000 = -1_000_000 + let elapsed_ms = 10_000u64.max(min_interval()); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(-100, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = (-100_i64) + .checked_mul(elapsed_ms_i64) + .expect("-100 * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + #[test] + fn new_observation_timestamp_is_clock_timestamp() { + let now = min_interval() + .checked_mul(2) + .expect("min_interval * 2 fits in u64"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(now), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.entries[1].timestamp, now); + } + + #[test] + fn write_index_advances_after_write() { + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.write_index, 2); + } + + #[test] + fn write_index_wraps_at_capacity() { + // write_index = CAPACITY - 1 → entry written to slot CAPACITY - 1 → write_index wraps to 0 + let capacity_minus_one = OBSERVATIONS_CAPACITY + .checked_sub(1) + .expect("OBSERVATIONS_CAPACITY is non-zero"); + let post_states = record_tick( + make_price_observations(capacity_minus_one, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.write_index, 0); + } + + #[test] + fn total_entries_incremented_after_write() { + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.total_entries, 2); + } + + #[test] + fn last_recorded_tick_updated_to_untruncated_current_tick() { + // Delta large enough to be clamped, but last_recorded_tick must store the raw tick. + let current_tick = MAX_TICK_DELTA + .checked_mul(3) + .expect("MAX_TICK_DELTA * 3 fits in i32"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(current_tick, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.last_recorded_tick, current_tick); + } + + #[test] + fn current_tick_and_clock_post_states_are_unchanged() { + let tick_account = make_current_tick_account(100, 0); + let clock = clock_account_with_timestamp(min_interval()); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + tick_account.clone(), + clock.clone(), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + assert_eq!(*post_states[1].account(), tick_account.account); + assert_eq!(*post_states[2].account(), clock.account); + } + + // ── write_index = 0 path ────────────────────────────────────────────────── + + #[test] + fn write_index_zero_reads_from_last_slot() { + // write_index = 0 means the last written entry is at CAPACITY - 1. + // Exercises the write_index == 0 branch in last_index computation. + // The new entry goes to slot 0 and write_index advances to 1. + let elapsed_ms = min_interval(); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let post_states = record_tick( + make_price_observations(0, 0, 0, 0), + make_current_tick_account(100, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = 100_i64 + .checked_mul(elapsed_ms_i64) + .expect("100 * elapsed_ms fits in i64"); + assert_eq!(obs.write_index, 1); + assert_eq!(obs.entries[0].timestamp, elapsed_ms); + assert_eq!(obs.entries[0].tick_cumulative, expected); + } + + // ── sampling guard ──────────────────────────────────────────────────────── + + #[test] + fn sampling_guard_skips_write_when_elapsed_below_min_interval() { + let before_interval = min_interval().checked_sub(1).expect("min_interval > 0"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(before_interval), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.write_index, 1); + assert_eq!(obs.total_entries, 1); + } + + #[test] + fn sampling_guard_does_not_update_last_recorded_tick() { + // When the guard fires, the entire observations account must be returned unchanged — + // including last_recorded_tick, which is the baseline for the next delta computation. + let before_interval = min_interval().checked_sub(1).expect("min_interval > 0"); + let post_states = record_tick( + make_price_observations(1, 500, 0, 0), + make_current_tick_account(999, 0), + clock_account_with_timestamp(before_interval), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.last_recorded_tick, 500); + } + + #[test] + fn sampling_guard_allows_write_at_exactly_min_interval() { + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + assert_eq!(obs.write_index, 2); + assert_eq!(obs.total_entries, 2); + } + + // ── tick-delta truncation ───────────────────────────────────────────────── + + #[test] + fn large_positive_delta_clamped_to_max_tick_delta() { + // current_tick = MAX_TICK_DELTA * 2, last_recorded_tick = 0 → clamped to MAX_TICK_DELTA + let elapsed_ms = min_interval(); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let double_max = MAX_TICK_DELTA + .checked_mul(2) + .expect("MAX_TICK_DELTA * 2 fits in i32"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(double_max, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = i64::from(MAX_TICK_DELTA) + .checked_mul(elapsed_ms_i64) + .expect("MAX_TICK_DELTA * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + #[test] + fn large_negative_delta_clamped_to_min_tick_delta() { + // current_tick = -(MAX_TICK_DELTA * 2), last_recorded_tick = 0 → clamped to -MAX_TICK_DELTA + let elapsed_ms = min_interval(); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let neg_double_max = MAX_TICK_DELTA + .checked_mul(2) + .and_then(|v| v.checked_neg()) + .expect("-(MAX_TICK_DELTA * 2) fits in i32"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(neg_double_max, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let neg_max = MAX_TICK_DELTA + .checked_neg() + .expect("negation of MAX_TICK_DELTA fits in i32"); + let expected = i64::from(neg_max) + .checked_mul(elapsed_ms_i64) + .expect("-MAX_TICK_DELTA * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + #[test] + fn delta_exactly_at_max_tick_delta_passes_through_untruncated() { + // delta == MAX_TICK_DELTA is at the boundary — clamp() must not reduce it. + let elapsed_ms = min_interval(); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(MAX_TICK_DELTA, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = i64::from(MAX_TICK_DELTA) + .checked_mul(elapsed_ms_i64) + .expect("MAX_TICK_DELTA * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + #[test] + fn small_delta_passes_through_untruncated() { + // current_tick = 100, last_recorded_tick = 0 → delta 100, well within MAX_TICK_DELTA + let elapsed_ms = min_interval(); + let elapsed_ms_i64 = i64::try_from(elapsed_ms).expect("elapsed_ms fits in i64"); + let post_states = record_tick( + make_price_observations(1, 0, 0, 0), + make_current_tick_account(100, 0), + clock_account_with_timestamp(elapsed_ms), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + let obs = PriceObservations::try_from(&post_states[0].account().data) + .expect("valid PriceObservations"); + let expected = 100_i64 + .checked_mul(elapsed_ms_i64) + .expect("100 * elapsed_ms fits in i64"); + assert_eq!(obs.entries[1].tick_cumulative, expected); + } + + // ── cross-source spoofing ───────────────────────────────────────────────── + + /// An attacker who controls their own price source cannot inject ticks into a victim's + /// observations account. They would pass the victim's `price_source_id` to match the + /// observations PDA, but their `current_tick_account` is derived from their own source ID, + /// so the current tick account PDA check fails. + #[test] + #[should_panic(expected = "current tick account ID does not match expected PDA")] + fn cannot_inject_tick_into_another_sources_observations() { + let attacker_source_id = AccountId::new([2u8; 32]); + let attacker_tick_account = AccountWithMetadata { + account: Account { + program_owner: ORACLE_PROGRAM_ID, + balance: 0, + data: Data::from(&CurrentTickAccount { + tick: 99_999, + last_updated: 0, + }), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: compute_current_tick_account_pda(ORACLE_PROGRAM_ID, attacker_source_id), + }; + record_tick( + make_price_observations(1, 0, 0, 0), /* victim's observations (price_source_id = + * [1u8;32]) */ + attacker_tick_account, + clock_account_with_timestamp(min_interval()), + price_source_id(), /* victim's price_source_id — attacker claims this to match + * observations */ + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + // ── precondition violations ─────────────────────────────────────────────── + + #[test] + #[should_panic(expected = "current tick account ID does not match expected PDA")] + fn wrong_current_tick_account_id_panics() { + let mut wrong = make_current_tick_account(0, 0); + wrong.account_id = AccountId::new([0u8; 32]); + record_tick( + make_price_observations(1, 0, 0, 0), + wrong, + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price observations account ID does not match expected PDA")] + fn wrong_price_observations_id_panics() { + let mut wrong = make_price_observations(1, 0, 0, 0); + wrong.account_id = AccountId::new([0u8; 32]); + record_tick( + wrong, + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "current tick account must be initialized")] + fn uninitialized_current_tick_account_panics() { + let uninit = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + }; + record_tick( + make_price_observations(1, 0, 0, 0), + uninit, + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price observations account must be initialized")] + fn uninitialized_price_observations_panics() { + let uninit = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_price_observations_pda( + ORACLE_PROGRAM_ID, + price_source_id(), + WINDOW_24H, + ), + }; + record_tick( + uninit, + make_current_tick_account(0, 0), + clock_account_with_timestamp(min_interval()), + price_source_id(), + WINDOW_24H, + ORACLE_PROGRAM_ID, + ); + } +} diff --git a/programs/twap_oracle/src/update_current_tick.rs b/programs/twap_oracle/src/update_current_tick.rs new file mode 100644 index 0000000..a63338b --- /dev/null +++ b/programs/twap_oracle/src/update_current_tick.rs @@ -0,0 +1,253 @@ +use clock_core::ClockAccountData; +use nssa_core::{ + account::{AccountWithMetadata, Data}, + program::{AccountPostState, ProgramId}, +}; +use twap_oracle_core::{compute_current_tick_account_pda, CurrentTickAccount}; + +/// Updates the tick stored in an existing [`CurrentTickAccount`]. +/// +/// # Panics +/// Panics if: +/// - `current_tick_account.account_id` does not match +/// `compute_current_tick_account_pda(oracle_program_id, price_source.account_id)`. +/// - `current_tick_account.account` is not a valid, initialised [`CurrentTickAccount`]. +/// - `price_source.is_authorized` is false. +pub fn update_current_tick( + current_tick_account: AccountWithMetadata, + price_source: AccountWithMetadata, + clock: AccountWithMetadata, + tick: i32, + oracle_program_id: ProgramId, +) -> Vec { + let price_source_id = price_source.account_id; + assert_eq!( + current_tick_account.account_id, + compute_current_tick_account_pda(oracle_program_id, price_source_id), + "UpdateCurrentTick: current tick account ID does not match expected PDA" + ); + assert!( + price_source.is_authorized, + "UpdateCurrentTick: price source account must be authorized" + ); + + let mut stored = CurrentTickAccount::try_from(¤t_tick_account.account.data) + .expect("UpdateCurrentTick: current tick account must be initialized"); + + let clock_data = ClockAccountData::from_bytes(clock.account.data.as_ref()); + stored.tick = tick; + stored.last_updated = clock_data.timestamp; + + let mut current_tick_account_post = current_tick_account.account.clone(); + current_tick_account_post.data = Data::from(&stored); + + vec![ + AccountPostState::new(current_tick_account_post), + AccountPostState::new(price_source.account.clone()), + AccountPostState::new(clock.account.clone()), + ] +} + +#[cfg(test)] +mod tests { + use nssa_core::account::{Account, AccountId, Nonce}; + use twap_oracle_core::compute_current_tick_account_pda; + + use super::*; + + const ORACLE_PROGRAM_ID: ProgramId = [77u32; 8]; + const CLOCK_PROGRAM_ID: ProgramId = [88u32; 8]; + + fn price_source_id() -> AccountId { + AccountId::new([1u8; 32]) + } + + fn clock_account_with_timestamp(timestamp: u64) -> AccountWithMetadata { + let data = ClockAccountData { + block_id: 0, + timestamp, + } + .to_bytes(); + AccountWithMetadata { + account: Account { + program_owner: CLOCK_PROGRAM_ID, + balance: 0, + data: Data::try_from(data).expect("ClockAccountData fits in Data"), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: AccountId::new([99u8; 32]), + } + } + + fn price_source_authorized() -> AccountWithMetadata { + AccountWithMetadata { + account: Account { + program_owner: [42u32; 8], + balance: 0, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: price_source_id(), + } + } + + fn current_tick_account_initialized(tick: i32, last_updated: u64) -> AccountWithMetadata { + let stored = CurrentTickAccount { tick, last_updated }; + AccountWithMetadata { + account: Account { + program_owner: ORACLE_PROGRAM_ID, + balance: 0, + data: Data::from(&stored), + nonce: Nonce(0), + }, + is_authorized: false, + account_id: compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + } + } + + // ── happy path ──────────────────────────────────────────────────────────── + + #[test] + fn returns_three_post_states() { + let post_states = update_current_tick( + current_tick_account_initialized(0, 0), + price_source_authorized(), + clock_account_with_timestamp(1_000), + 100, + ORACLE_PROGRAM_ID, + ); + assert_eq!(post_states.len(), 3); + } + + #[test] + fn tick_updated_to_new_value() { + let post_states = update_current_tick( + current_tick_account_initialized(100, 0), + price_source_authorized(), + clock_account_with_timestamp(1_000), + 200, + ORACLE_PROGRAM_ID, + ); + let account = CurrentTickAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid CurrentTickAccount"); + assert_eq!(account.tick, 200); + } + + #[test] + fn timestamp_updated_from_clock() { + let post_states = update_current_tick( + current_tick_account_initialized(0, 0), + price_source_authorized(), + clock_account_with_timestamp(999_000), + 0, + ORACLE_PROGRAM_ID, + ); + let account = CurrentTickAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid CurrentTickAccount"); + assert_eq!(account.last_updated, 999_000); + } + + #[test] + fn positive_and_negative_ticks_accepted() { + for tick in [i32::MIN, -1, 0, 1, i32::MAX] { + let post_states = update_current_tick( + current_tick_account_initialized(0, 0), + price_source_authorized(), + clock_account_with_timestamp(0), + tick, + ORACLE_PROGRAM_ID, + ); + let account = CurrentTickAccount::try_from(&post_states[0].account().data) + .expect("post state must contain a valid CurrentTickAccount"); + assert_eq!(account.tick, tick); + } + } + + #[test] + fn price_source_and_clock_post_states_are_unchanged() { + let price_source = price_source_authorized(); + let clock = clock_account_with_timestamp(42_000); + + let post_states = update_current_tick( + current_tick_account_initialized(0, 0), + price_source.clone(), + clock.clone(), + 0, + ORACLE_PROGRAM_ID, + ); + + assert_eq!(*post_states[1].account(), price_source.account); + assert_eq!(*post_states[2].account(), clock.account); + } + + // ── precondition violations ─────────────────────────────────────────────── + + #[test] + #[should_panic(expected = "current tick account ID does not match expected PDA")] + fn wrong_account_id_panics() { + let mut wrong = current_tick_account_initialized(0, 0); + wrong.account_id = AccountId::new([0u8; 32]); + update_current_tick( + wrong, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "current tick account must be initialized")] + fn uninitialized_account_panics() { + let uninit = AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: compute_current_tick_account_pda(ORACLE_PROGRAM_ID, price_source_id()), + }; + update_current_tick( + uninit, + price_source_authorized(), + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } + + #[test] + #[should_panic(expected = "price source account must be authorized")] + fn unauthorized_price_source_panics() { + let mut unauthorized = price_source_authorized(); + unauthorized.is_authorized = false; + update_current_tick( + current_tick_account_initialized(0, 0), + unauthorized, + clock_account_with_timestamp(0), + 0, + ORACLE_PROGRAM_ID, + ); + } + + /// An attacker who controls their own price source cannot update a different (victim's) + /// current tick account. The PDA is derived from the price source ID, so presenting an + /// authorized attacker source against the victim's account ID will always fail the PDA check. + #[test] + #[should_panic(expected = "current tick account ID does not match expected PDA")] + fn cannot_update_another_price_sources_tick_account() { + let victim_source_id = AccountId::new([2u8; 32]); + let victim_account_id = + compute_current_tick_account_pda(ORACLE_PROGRAM_ID, victim_source_id); + + let mut victim_account = current_tick_account_initialized(500, 1_000); + victim_account.account_id = victim_account_id; + + update_current_tick( + victim_account, + price_source_authorized(), // attacker controls price_source_id = [1u8; 32] + clock_account_with_timestamp(2_000), + 999, + ORACLE_PROGRAM_ID, + ); + } +}