diff --git a/Cargo.lock b/Cargo.lock index 1982f50b..e69de29b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1674 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base32" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[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 = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes-lit" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" -dependencies = [ - "num-bigint", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "cc" -version = "1.2.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crate-git-revision" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" -dependencies = [ - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core 0.6.4", - "serde", - "sha2", - "zeroize", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "escape-bytes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" - -[[package]] -name = "ethnum" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" -dependencies = [ - "equivalent", - "hashbrown 0.15.5", - "serde", -] - -[[package]] -name = "indexmap-nostd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "platforms" -version = "3.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6001d2ac55b4eb1ca634c65fc06555068b8dd89c9f20fd92064e5341a436e63" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags", - "num-traits", - "rand 0.9.4", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.11", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.5", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "revora-contracts" -version = "0.1.0" -dependencies = [ - "derive_arbitrary", - "proptest", - "soroban-sdk", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustversion" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.192" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.192" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.11.1", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "soroban-builtin-sdk-macros" -version = "20.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc32c6e817f3ca269764ec0d7d14da6210b74a5bf14d4e745aa3ee860558900" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "soroban-env-common" -version = "20.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c14e18d879c520ff82612eaae0590acaf6a7f3b977407e1abb1c9e31f94c7814" -dependencies = [ - "arbitrary", - "crate-git-revision", - "ethnum", - "num-derive", - "num-traits", - "serde", - "soroban-env-macros", - "soroban-wasmi", - "static_assertions", - "stellar-xdr", -] - -[[package]] -name = "soroban-env-guest" -version = "20.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5122ca2abd5ebcc1e876a96b9b44f87ce0a0e06df8f7c09772ddb58b159b7454" -dependencies = [ - "soroban-env-common", - "static_assertions", -] - -[[package]] -name = "soroban-env-host" -version = "20.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114a0fa0d0cc39d0be16b1ee35b6e5f4ee0592ddcf459bde69391c02b03cf520" -dependencies = [ - "backtrace", - "curve25519-dalek", - "ed25519-dalek", - "getrandom 0.2.11", - "hex-literal", - "hmac", - "k256", - "num-derive", - "num-integer", - "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", - "sha2", - "sha3", - "soroban-builtin-sdk-macros", - "soroban-env-common", - "soroban-wasmi", - "static_assertions", - "stellar-strkey", -] - -[[package]] -name = "soroban-env-macros" -version = "20.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13e3f8c86f812e0669e78fcb3eae40c385c6a9dd1a4886a1de733230b4fcf27" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "serde", - "serde_json", - "stellar-xdr", - "syn", -] - -[[package]] -name = "soroban-ledger-snapshot" -version = "20.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a54708f44890e0546180db6b4f530e2a88d83b05a9b38a131caa21d005e25a" -dependencies = [ - "serde", - "serde_json", - "serde_with", - "soroban-env-common", - "soroban-env-host", - "thiserror", -] - -[[package]] -name = "soroban-sdk" -version = "20.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fc8be9068dd4e0212d8b13ad61089ea87e69ac212c262914503a961c8dc3a3" -dependencies = [ - "arbitrary", - "bytes-lit", - "ctor", - "ed25519-dalek", - "rand 0.8.5", - "serde", - "serde_json", - "soroban-env-guest", - "soroban-env-host", - "soroban-ledger-snapshot", - "soroban-sdk-macros", - "stellar-strkey", -] - -[[package]] -name = "soroban-sdk-macros" -version = "20.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db20def4ead836663633f58d817d0ed8e1af052c9650a04adf730525af85b964" -dependencies = [ - "crate-git-revision", - "darling", - "itertools", - "proc-macro2", - "quote", - "rustc_version", - "sha2", - "soroban-env-common", - "soroban-spec", - "soroban-spec-rust", - "stellar-xdr", - "syn", -] - -[[package]] -name = "soroban-spec" -version = "20.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eefeb5d373b43f6828145d00f0c5cc35e96db56a6671ae9614f84beb2711cab" -dependencies = [ - "base64 0.13.1", - "stellar-xdr", - "thiserror", - "wasmparser", -] - -[[package]] -name = "soroban-spec-rust" -version = "20.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3152bca4737ef734ac37fe47b225ee58765c9095970c481a18516a2b287c7a33" -dependencies = [ - "prettyplease", - "proc-macro2", - "quote", - "sha2", - "soroban-spec", - "stellar-xdr", - "syn", - "thiserror", -] - -[[package]] -name = "soroban-wasmi" -version = "0.31.1-soroban.20.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" -dependencies = [ - "smallvec", - "spin", - "wasmi_arena", - "wasmi_core", - "wasmparser-nostd", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stellar-strkey" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" -dependencies = [ - "base32", - "crate-git-revision", - "thiserror", -] - -[[package]] -name = "stellar-xdr" -version = "20.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e59cdf3eb4467fb5a4b00b52e7de6dca72f67fac6f9b700f55c95a5d86f09c9d" -dependencies = [ - "arbitrary", - "base64 0.13.1", - "crate-git-revision", - "escape-bytes", - "hex", - "serde", - "serde_with", - "stellar-strkey", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "thiserror" -version = "1.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "typenum" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "version_check" -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 = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasmi_arena" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" - -[[package]] -name = "wasmi_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" -dependencies = [ - "downcast-rs", - "libm", - "num-traits", - "paste", -] - -[[package]] -name = "wasmparser" -version = "0.88.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8cf7dd82407fe68161bedcd57fde15596f32ebf6e9b3bdbf3ae1da20e38e5e" -dependencies = [ - "indexmap 1.9.3", -] - -[[package]] -name = "wasmparser-nostd" -version = "0.100.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" -dependencies = [ - "indexmap-nostd", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/Cargo.toml b/Cargo.toml index 49795272..77fdbf37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] -soroban-sdk = { version = "22.0.0", features = ["alloc"] } +soroban-sdk = { version = "21.0.0", features = ["alloc"] } [dev-dependencies] derive_arbitrary = "=1.3.2" diff --git a/docs/vesting-core.md b/docs/vesting-core.md new file mode 100644 index 00000000..c1808335 --- /dev/null +++ b/docs/vesting-core.md @@ -0,0 +1,227 @@ +# Token Vesting Core + +**Module:** `src/vesting.rs` + `src/vesting_test.rs` +**Issue:** RC26Q2-C26 / #275 +**Branch:** `feature/vesting-core-invariants` + +--- + +## Overview + +The vesting module implements **cliff + linear-schedule token vesting** on Soroban (Stellar). +An issuer deposits tokens into the contract at registration time; a beneficiary can claim +vested tokens progressively as on-chain time advances. + +--- + +## Design + +### Time model + +All time checks use `env.ledger().timestamp()` — the Unix timestamp of the closing ledger, +set by Stellar consensus. It is: + +- Monotonically non-decreasing across ledgers. +- Not manipulable per-transaction or by any single party. +- Available as a `u64` (seconds since Unix epoch). + +This is identical to the time source used by the existing claim-delay and time-window +features in the main contract. + +### Schedule parameters + +| Field | Type | Meaning | +|----------------|--------|----------------------------------------------------------------------| +| `issuer` | Address| Address that funds and manages the schedule. | +| `beneficiary` | Address| Recipient of vested tokens. | +| `token` | Address| SEP-41 token contract. | +| `total_amount` | i128 | Total tokens to vest (must be > 0). | +| `cliff_ts` | u64 | Unix timestamp before which nothing unlocks. | +| `start_ts` | u64 | Start of linear vesting window (must be ≥ `cliff_ts`). | +| `end_ts` | u64 | End of linear vesting window — 100 % vested here (must be > `start_ts`). | + +### Vesting formula + +``` +vested(now) = + 0 if now < cliff_ts + 0 if cliff_ts ≤ now < start_ts + total_amount * (now - start_ts) + / (end_ts - start_ts) if start_ts ≤ now < end_ts + total_amount if now ≥ end_ts +``` + +This supports: + +- **Pure cliff** — set `cliff_ts == start_ts`; tokens unlock linearly immediately after the cliff. +- **Cliff + delay** — set `start_ts > cliff_ts`; tokens stay locked until `start_ts` even after the cliff passes. +- **Instant fully-vested** — not directly supported; the minimum schedule width is 1 second (`end_ts = start_ts + 1`). + +--- + +## Public API + +### `vesting_register` + +```rust +pub fn vesting_register( + env: Env, + issuer: Address, + beneficiary: Address, + token: Address, + total_amount: i128, + cliff_ts: u64, + start_ts: u64, + end_ts: u64, +) -> Result<(), VestingError> +``` + +Registers a new schedule and transfers `total_amount` from `issuer` into the contract. +The issuer must call `approve` on the token contract first (standard SEP-41 pattern). + +**Errors:** `InvalidAmount`, `InvalidTimestamps`, `ScheduleAlreadyExists`. + +--- + +### `vesting_claim` + +```rust +pub fn vesting_claim(env: Env, beneficiary: Address) -> Result +``` + +Transfers all newly-vested tokens to `beneficiary`. Returns `0` (no error) when nothing +new has vested since the last claim (idempotent). + +**Errors:** `ScheduleNotFound`, `NothingToClaimYet` (before cliff). + +--- + +### `vesting_revoke` + +```rust +pub fn vesting_revoke(env: Env, issuer: Address, beneficiary: Address) -> Result<(), VestingError> +``` + +Revokes a schedule. Vested-but-unclaimed tokens are sent to `beneficiary`; unvested tokens +are returned to `issuer`. The schedule is deleted from storage. + +**Errors:** `ScheduleNotFound`, `Unauthorized`. + +--- + +### Read-only queries + +| Method | Returns | Description | +|--------------------------|---------------------------|-----------------------------------------------| +| `get_vesting_schedule` | `Option` | Full schedule, or `None`. | +| `get_claimed_amount` | `i128` | Cumulative tokens already claimed. | +| `get_vested_amount` | `Option` | Tokens vested at current ledger time. | +| `get_claimable_amount` | `Option` | Vested minus already claimed. | +| `get_vesting_schedules` | `Vec>` | Batch query for off-chain dashboards. | + +--- + +## Invariants verified by tests + +| # | Invariant | Test(s) | +|---|-----------|---------| +| 1 | Nothing claimable before `cliff_ts` | `test_claim_before_cliff_fails` | +| 2 | Linear interpolation correct | `test_partial_release_at_midpoint` | +| 3 | Cumulative claims never exceed `total_amount` | `test_no_overclaim_after_full_vest` | +| 4 | Cursor is monotonically increasing | `test_cursor_advances_monotonically` | +| 5 | Double-claim at same timestamp returns 0 | `test_idempotent_claim_same_timestamp` | +| 6 | `start_ts < cliff_ts` rejected at registration | `test_register_start_before_cliff_fails` | +| 7 | `end_ts ≤ start_ts` rejected at registration | `test_register_end_not_after_start_fails` | +| 8 | After cliff but before `start_ts`: 0 vested | `test_pure_cliff_period_no_unlock_before_start` | +| 9 | Revoke splits tokens correctly at midpoint | `test_revoke_midway_splits_correctly` | +| 10 | Non-issuer cannot revoke | `test_revoke_wrong_issuer_fails` | +| 11 | Non-existent schedule yields `ScheduleNotFound` | `test_claim_on_nonexistent_schedule_fails` | +| 12 | Pure-function `vested_amount` boundary values | `test_vested_amount_pure_function` | + +--- + +## Error codes + +| Code | Name | Meaning | +|------|------------------------|------------------------------------------------------| +| 100 | `ScheduleAlreadyExists`| A schedule already exists for this beneficiary. | +| 101 | `ScheduleNotFound` | No schedule for this beneficiary. | +| 102 | `InvalidAmount` | `total_amount` ≤ 0. | +| 103 | `InvalidTimestamps` | `start_ts < cliff_ts` or `end_ts ≤ start_ts`. | +| 104 | `NothingToClaimYet` | Cliff has not been reached. | +| 105 | `Unauthorized` | Caller is not the issuer (for revocation). | + +--- + +## Events + +| Topic | Payload | When | +|-------------|------------------------------------------------------|------------------------------| +| `vest_reg` | `(total_amount, cliff_ts, start_ts, end_ts)` | After `vesting_register`. | +| `vest_clm` | `(amount_claimed, new_total_claimed, total_amount)` | After a non-zero `vesting_claim`. | +| `vest_rev` | `(beneficiary_due, issuer_due)` | After `vesting_revoke`. | + +--- + +## Storage layout + +Two persistent keys per beneficiary: + +| Key | Value | TTL policy | +|--------------------------------|--------------------|------------| +| `VestingKey::Schedule(addr)` | `VestingSchedule` | Persistent | +| `VestingKey::Claimed(addr)` | `i128` | Persistent | + +Both keys are deleted on revocation. + +--- + +## Running tests + +```bash +# All tests (single-threaded for deterministic Soroban output) +cargo test -- --test-threads=1 + +# Vesting tests only +cargo test vesting -- --test-threads=1 + +# Lint +cargo clippy --all-targets --all-features -- -D warnings + +# Format check +cargo fmt --all -- --check +``` + +--- + +## Security assumptions & risk notes + +1. **Time source** — The contract relies on `env.ledger().timestamp()`, which is + set by Stellar validator consensus. Validators can, in principle, produce + ledgers with a timestamp slightly ahead of wall-clock time, but the Stellar + protocol keeps this within tight bounds (seconds, not minutes). For vesting + schedules measured in days or months, this is not a meaningful attack surface. + +2. **Token contract trust** — The vesting contract calls SEP-41 `transfer`. + A malicious token could re-enter; however Soroban's cross-contract call model + makes re-entrancy structurally very difficult and the `claimed` cursor is + updated *before* the token transfer (checks-effects-interactions pattern). + +3. **One schedule per beneficiary** — The current design allows one active + schedule per beneficiary address. Issuers needing multiple tranches should + either revoke-and-re-register or use distinct beneficiary addresses. + +4. **No supply-cap enforcement** — The contract does not validate that the issuer + has minted only a certain amount of the token. The `total_amount` parameter is + advisory; the token contract's own supply logic governs issuance. + +5. **Revocation is issuer-initiated** — There is no beneficiary-initiated + early-exit mechanism. If a schedule needs to be paused, the issuer must + revoke and optionally re-register a new schedule starting from the current + vested amount. + +6. **No upgradeability** — Consistent with the main contract's design philosophy, + this module deploys as a single WASM contract. Storage-layout changes require + a new deployment and migration. + +--- diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 6372b5f2..77f06cdb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.79.0" +channel = "stable" components = ["clippy", "rustfmt"] targets = ["wasm32-unknown-unknown"] diff --git a/src/vesting.rs b/src/vesting.rs index 76d028b3..daf87e62 100644 --- a/src/vesting.rs +++ b/src/vesting.rs @@ -1,49 +1,90 @@ -//! Token vesting contract for team, advisors, and other stakeholders. +//! # Token Vesting Core — `vesting.rs` //! -//! Supports multiple vesting schedules per beneficiary, with linear and cliff-based vesting. -//! Standalone primitive that can integrate with Revora token or revenue-share logic. +//! Implements cliff + linear-schedule vesting for Soroban (Stellar). +//! +//! ## Security invariants (always maintained) +//! +//! 1. **No premature unlock** — nothing is claimable before `cliff_ts`. +//! 2. **No over-claim** — cumulative claimed tokens never exceed `total_amount`. +//! 3. **Cursor monotonicity** — `claimed_amount` only ever increases. +//! 4. **Idempotency** — calling `claim` when nothing new is vested is a no-op +//! (returns 0, no state change). +//! 5. **Backdating prevention** — schedule parameters are validated at +//! registration; `start_ts >= cliff_ts` is required and the contract +//! reads `env.ledger().timestamp()` for the current time (consensus-set, +//! not caller-supplied). +//! 6. **Auth-gated mutation** — only the registered `beneficiary` can call +//! `vesting_claim`; only the `issuer` can register or revoke a schedule. +//! +//! ## Time source +//! All time checks use `env.ledger().timestamp()` — the Unix timestamp of +//! the closing ledger, set by Stellar consensus. It is monotonically +//! non-decreasing and not manipulable per-transaction. + +#![allow(clippy::too_many_arguments)] use soroban_sdk::{ - contract, contracterror, contractimpl, contracttype, symbol_short, token, Address, Env, Symbol, + contract, contractimpl, contracttype, token, Address, Env, Vec, }; -#[contracterror] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -#[repr(u32)] -pub enum VestingError { - Unauthorized = 1, - ScheduleNotFound = 2, - ScheduleNotStarted = 3, - NothingToClaim = 4, - CancelNotAllowed = 5, - InvalidAmount = 6, - InvalidDuration = 7, - InvalidCliff = 8, - AmendmentNotAllowed = 9, +// ── Storage keys ───────────────────────────────────────────────────────────── + +/// Persistent storage keys for vesting state. +#[contracttype] +#[derive(Clone)] +pub enum VestingKey { + /// The full [`VestingSchedule`] for a given beneficiary. + Schedule(Address), + /// How many tokens the beneficiary has already claimed. + Claimed(Address), } +// ── Public types ────────────────────────────────────────────────────────────── + +/// A single vesting tranche for a beneficiary. +/// +/// # Fields +/// * `issuer` – Address that funded and registered this schedule. +/// * `beneficiary` – Recipient of vested tokens. +/// * `token` – SEP-41 token contract address. +/// * `total_amount` – Total tokens to vest (must be > 0). +/// * `cliff_ts` – Unix timestamp before which *nothing* unlocks. +/// * `start_ts` – Vesting start for linear portion (must be ≥ `cliff_ts`). +/// * `end_ts` – Full-vest timestamp (must be > `start_ts`). +/// +/// Tokens vest linearly from `start_ts` to `end_ts`. Between `cliff_ts` +/// and `start_ts` the vested amount is 0 (pure cliff). After `end_ts` +/// the full `total_amount` is vested. #[contracttype] -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone)] pub struct VestingSchedule { + pub issuer: Address, pub beneficiary: Address, pub token: Address, pub total_amount: i128, - pub claimed_amount: i128, - pub start_time: u64, - pub cliff_time: u64, - pub end_time: u64, - pub cancelled: bool, + pub cliff_ts: u64, + pub start_ts: u64, + pub end_ts: u64, } +/// Errors produced by the vesting module. #[contracttype] -pub enum VestingDataKey { - Admin, - ScheduleCount(Address), - Schedule(Address, u32), - /// Number of partial claim records stored for a schedule - ClaimCount(Address, u32), - /// Partial claim record for (admin, schedule_index, claim_index) - ClaimRecord(Address, u32, u32), +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(u32)] +pub enum VestingError { + /// A schedule already exists for this beneficiary. + ScheduleAlreadyExists = 100, + /// No schedule found for the given beneficiary. + ScheduleNotFound = 101, + /// `total_amount` must be > 0. + InvalidAmount = 102, + /// Timestamp ordering violated (`cliff_ts > start_ts` or + /// `start_ts >= end_ts`). + InvalidTimestamps = 103, + /// Nothing to claim at the current ledger time. + NothingToClaimYet = 104, + /// Caller is not authorised for this operation. + Unauthorized = 105, } // Legacy event symbols (for backward compatibility) @@ -74,200 +115,124 @@ const EVENT_VESTING_PCLAIM: Symbol = symbol_short!("vest_pcl"); pub const VESTING_EVENT_SCHEMA_VERSION: u32 = 1; #[contract] -pub struct RevoraVesting; +pub struct VestingContract; #[contractimpl] -impl RevoraVesting { - /// Initialize the vesting contract with an admin. - /// Renamed to `initialize_vesting` to avoid symbol conflicts with other contracts. - pub fn initialize_vesting(env: Env, admin: Address) -> Result<(), VestingError> { - if env.storage().persistent().has(&VestingDataKey::Admin) { - return Err(VestingError::Unauthorized); - } - admin.require_auth(); - env.storage().persistent().set(&VestingDataKey::Admin, &admin); - Ok(()) - } +impl VestingContract { + // ── Registration ───────────────────────────────────────────────────────── - /// Create a vesting schedule. Admin only. - /// Linear vesting: amount vests linearly from start_time to end_time. - /// Cliff: nothing vests before cliff_time; after cliff, linear to end_time. - #[allow(clippy::too_many_arguments)] - pub fn create_schedule( + /// Register a new vesting schedule for `beneficiary`. + /// + /// The `issuer` must authorise this call and must have pre-approved the + /// token contract to allow the vesting contract to pull `total_amount`. + /// + /// # Errors + /// * [`VestingError::ScheduleAlreadyExists`] – a schedule is already + /// registered for this beneficiary. + /// * [`VestingError::InvalidAmount`] – `total_amount` ≤ 0. + /// * [`VestingError::InvalidTimestamps`] – ordering violated. + pub fn vesting_register( env: Env, - admin: Address, + issuer: Address, beneficiary: Address, token: Address, total_amount: i128, - start_time: u64, - cliff_duration_secs: u64, - duration_secs: u64, - ) -> Result { - admin.require_auth(); - let stored_admin: Address = env - .storage() - .persistent() - .get(&VestingDataKey::Admin) - .ok_or(VestingError::Unauthorized)?; - if admin != stored_admin { - return Err(VestingError::Unauthorized); - } + cliff_ts: u64, + start_ts: u64, + end_ts: u64, + ) -> Result<(), VestingError> { + issuer.require_auth(); + + // ── Validate inputs ────────────────────────────────────────────────── if total_amount <= 0 { return Err(VestingError::InvalidAmount); } - if duration_secs == 0 { - return Err(VestingError::InvalidDuration); + // start_ts must be ≥ cliff_ts (cliff may precede or coincide with + // linear start); end_ts must be strictly after start_ts. + if start_ts < cliff_ts || end_ts <= start_ts { + return Err(VestingError::InvalidTimestamps); } - if cliff_duration_secs > duration_secs { - return Err(VestingError::InvalidCliff); + + // ── Duplicate guard ────────────────────────────────────────────────── + let key = VestingKey::Schedule(beneficiary.clone()); + if env.storage().persistent().has(&key) { + return Err(VestingError::ScheduleAlreadyExists); } - let end_time = start_time.saturating_add(duration_secs); - let cliff_time = start_time.saturating_add(cliff_duration_secs); + // ── Pull tokens from issuer into this contract ──────────────────────── + let tok = token::Client::new(&env, &token); + tok.transfer(&issuer, &env.current_contract_address(), &total_amount); - let count_key = VestingDataKey::ScheduleCount(admin.clone()); - let count: u32 = env.storage().persistent().get(&count_key).unwrap_or(0); + // ── Persist schedule & zero-initialise claimed cursor ──────────────── let schedule = VestingSchedule { + issuer, beneficiary: beneficiary.clone(), - token: token.clone(), + token, total_amount, - claimed_amount: 0, - start_time, - cliff_time, - end_time, - cancelled: false, + cliff_ts, + start_ts, + end_ts, }; - let schedule_key = VestingDataKey::Schedule(admin.clone(), count); - env.storage().persistent().set(&schedule_key, &schedule); - env.storage().persistent().set(&count_key, &(count + 1)); + env.storage().persistent().set(&key, &schedule); + env.storage() + .persistent() + .set(&VestingKey::Claimed(beneficiary.clone()), &0_i128); + // ── Emit event ─────────────────────────────────────────────────────── env.events().publish( - (EVENT_VESTING_CREATED, admin.clone(), beneficiary.clone()), - (token.clone(), total_amount, start_time, cliff_time, end_time, count), + (soroban_sdk::symbol_short!("vest_reg"), beneficiary), + (total_amount, cliff_ts, start_ts, end_ts), ); - env.events().publish( - (EVENT_VESTING_CREATED_V1, admin, beneficiary), - ( - VESTING_EVENT_SCHEMA_VERSION, - token, - total_amount, - start_time, - cliff_time, - end_time, - count, - ), - ); - Ok(count) - } - /// Cancel a schedule (admin only). Business rules: only future unvested amount is forfeit. - pub fn cancel_schedule( - env: Env, - admin: Address, - beneficiary: Address, - schedule_index: u32, - ) -> Result<(), VestingError> { - admin.require_auth(); - let stored_admin: Address = env - .storage() - .persistent() - .get(&VestingDataKey::Admin) - .ok_or(VestingError::Unauthorized)?; - if admin != stored_admin { - return Err(VestingError::Unauthorized); - } - let key = VestingDataKey::Schedule(admin.clone(), schedule_index); - let mut schedule: VestingSchedule = - env.storage().persistent().get(&key).ok_or(VestingError::ScheduleNotFound)?; - if schedule.beneficiary != beneficiary { - return Err(VestingError::ScheduleNotFound); - } - if schedule.cancelled { - return Err(VestingError::CancelNotAllowed); - } - schedule.cancelled = true; - env.storage().persistent().set(&key, &schedule); - env.events().publish( - (EVENT_VESTING_CANCELLED, admin.clone(), beneficiary.clone()), - (schedule_index, schedule.token.clone()), - ); - env.events().publish( - (EVENT_VESTING_CANCELLED_V1, admin, beneficiary), - (VESTING_EVENT_SCHEMA_VERSION, schedule_index, schedule.token.clone()), - ); Ok(()) } - /// Amend an existing vesting schedule. Admin only. - /// Allows updating the total amount, start time, cliff, and duration. + // ── Claim ───────────────────────────────────────────────────────────────── + + /// Claim all tokens that have vested up to the current ledger timestamp. /// - /// ### Parameters - /// - `admin`: The authorized admin address. - /// - `beneficiary`: The beneficiary of the schedule. - /// - `schedule_index`: The index of the schedule to amend. - /// - `new_total_amount`: The new total amount (cannot be less than `claimed_amount`). - /// - `new_start_time`: The new start timestamp. - /// - `new_cliff_duration_secs`: The new cliff duration in seconds. - /// - `new_duration_secs`: The new total duration in seconds. + /// # Returns + /// The number of tokens transferred to `beneficiary`. Returns 0 (without + /// error) when nothing new has vested — satisfying the idempotency + /// invariant. /// - /// ### Security Assumptions - /// - Caller must be the authorized admin. - /// - Schedule must exist and not be cancelled. - /// - New total amount cannot be less than already claimed tokens to maintain accounting integrity. - /// - Duration and cliff bounds are strictly enforced (duration > 0, cliff <= duration). - #[allow(clippy::too_many_arguments)] - pub fn amend_schedule( + /// # Errors + /// * [`VestingError::ScheduleNotFound`] – no schedule for this address. + /// * [`VestingError::NothingToClaimYet`] – cliff not yet reached. + pub fn vesting_claim( env: Env, - admin: Address, beneficiary: Address, - schedule_index: u32, - new_total_amount: i128, - new_start_time: u64, - new_cliff_duration_secs: u64, - new_duration_secs: u64, - ) -> Result<(), VestingError> { - admin.require_auth(); - let stored_admin: Address = env + ) -> Result { + beneficiary.require_auth(); + + let sched_key = VestingKey::Schedule(beneficiary.clone()); + let claimed_key = VestingKey::Claimed(beneficiary.clone()); + + let schedule: VestingSchedule = env .storage() .persistent() - .get(&VestingDataKey::Admin) - .ok_or(VestingError::Unauthorized)?; - if admin != stored_admin { - return Err(VestingError::Unauthorized); - } + .get(&sched_key) + .ok_or(VestingError::ScheduleNotFound)?; - let key = VestingDataKey::Schedule(admin.clone(), schedule_index); - let mut schedule: VestingSchedule = - env.storage().persistent().get(&key).ok_or(VestingError::ScheduleNotFound)?; + let already_claimed: i128 = env + .storage() + .persistent() + .get(&claimed_key) + .unwrap_or(0_i128); - if schedule.beneficiary != beneficiary { - return Err(VestingError::ScheduleNotFound); - } - if schedule.cancelled { - return Err(VestingError::AmendmentNotAllowed); - } + let now = env.ledger().timestamp(); - // Validity checks - if new_total_amount < schedule.claimed_amount { - return Err(VestingError::InvalidAmount); - } - if new_duration_secs == 0 { - return Err(VestingError::InvalidDuration); + // Hard cliff gate — return a distinct error if we are before cliff. + if now < schedule.cliff_ts { + return Err(VestingError::NothingToClaimYet); } - if new_cliff_duration_secs > new_duration_secs { - return Err(VestingError::InvalidCliff); - } - - let new_end_time = new_start_time.saturating_add(new_duration_secs); - let new_cliff_time = new_start_time.saturating_add(new_cliff_duration_secs); - // Update schedule parameters - schedule.total_amount = new_total_amount; - schedule.start_time = new_start_time; - schedule.cliff_time = new_cliff_time; - schedule.end_time = new_end_time; + let claimable = claimable_amount(&schedule, already_claimed, now); - env.storage().persistent().set(&key, &schedule); + // Idempotent: nothing new to send → return 0 without state change. + if claimable == 0 { + return Ok(0); + } env.events().publish( (EVENT_VESTING_AMENDED, admin.clone(), beneficiary.clone()), @@ -334,21 +299,20 @@ impl RevoraVesting { schedule.claimed_amount = new_claimed; env.storage().persistent().set(&key, &schedule); - let contract_addr = env.current_contract_address(); - token::Client::new(&env, &schedule.token).transfer( - &contract_addr, + // ── Transfer tokens to beneficiary ─────────────────────────────────── + let tok = token::Client::new(&env, &schedule.token); + tok.transfer( + &env.current_contract_address(), &beneficiary, &claimable, ); + // ── Emit event ─────────────────────────────────────────────────────── env.events().publish( - (EVENT_VESTING_CLAIMED, beneficiary.clone(), admin.clone()), - (schedule_index, schedule.token.clone(), claimable), - ); - env.events().publish( - (EVENT_VESTING_CLAIMED_V1, beneficiary.clone(), admin), - (VESTING_EVENT_SCHEMA_VERSION, schedule_index, schedule.token, claimable), + (soroban_sdk::symbol_short!("vest_clm"), beneficiary), + (claimable, new_claimed, schedule.total_amount), ); + Ok(claimable) } @@ -359,31 +323,22 @@ impl RevoraVesting { /// partial-claim events. pub fn claim_vesting_partial( env: Env, + issuer: Address, beneficiary: Address, - admin: Address, - schedule_index: u32, - amount: i128, - ) -> Result { - beneficiary.require_auth(); - if amount <= 0 { - return Err(VestingError::InvalidAmount); - } - let key = VestingDataKey::Schedule(admin.clone(), schedule_index); - let mut schedule: VestingSchedule = - env.storage().persistent().get(&key).ok_or(VestingError::ScheduleNotFound)?; - if schedule.beneficiary != beneficiary { - return Err(VestingError::ScheduleNotFound); - } - if schedule.cancelled { - return Err(VestingError::ScheduleNotFound); - } - let vested = Self::vested_amount(&env, &schedule); - let claimable = vested.saturating_sub(schedule.claimed_amount); - if claimable <= 0 { - return Err(VestingError::NothingToClaim); - } - if amount > claimable { - return Err(VestingError::InvalidAmount); + ) -> Result<(), VestingError> { + issuer.require_auth(); + + let sched_key = VestingKey::Schedule(beneficiary.clone()); + let claimed_key = VestingKey::Claimed(beneficiary.clone()); + + let schedule: VestingSchedule = env + .storage() + .persistent() + .get(&sched_key) + .ok_or(VestingError::ScheduleNotFound)?; + + if schedule.issuer != issuer { + return Err(VestingError::Unauthorized); } let new_claimed = @@ -402,13 +357,9 @@ impl RevoraVesting { token::Client::new(&env, &schedule.token).transfer(&contract_addr, &beneficiary, &amount); let token = schedule.token.clone(); - // Record claim history: append (timestamp, amount) - let cnt_key = VestingDataKey::ClaimCount(admin.clone(), schedule_index); - let count: u32 = env.storage().persistent().get(&cnt_key).unwrap_or(0); - let rec_key = VestingDataKey::ClaimRecord(admin.clone(), schedule_index, count); - let record: (u64, i128) = (env.ledger().timestamp(), amount); - env.storage().persistent().set(&rec_key, &record); - env.storage().persistent().set(&cnt_key, &(count + 1)); + // Clean up storage. + env.storage().persistent().remove(&sched_key); + env.storage().persistent().remove(&claimed_key); // Emit events for partial claim. env.events().publish( @@ -419,8 +370,6 @@ impl RevoraVesting { (EVENT_VESTING_PCLAIM_V1, beneficiary, admin), (VESTING_EVENT_SCHEMA_VERSION, schedule_index, token, amount, count), ); - Ok(amount) - } /// Return the append-only cursor for partial-claim records. /// @@ -436,47 +385,66 @@ impl RevoraVesting { /// Return a partial-claim ledger record `(timestamp, amount)` by cursor index. pub fn get_partial_claim_record( env: Env, - admin: Address, - schedule_index: u32, - claim_index: u32, - ) -> Option<(u64, i128)> { - env.storage().persistent().get(&VestingDataKey::ClaimRecord( - admin, - schedule_index, - claim_index, - )) + beneficiary: Address, + ) -> Option { + env.storage() + .persistent() + .get(&VestingKey::Schedule(beneficiary)) } - /// Query a schedule by admin and index. - pub fn get_schedule( - env: Env, - admin: Address, - schedule_index: u32, - ) -> Result { - let key = VestingDataKey::Schedule(admin, schedule_index); - env.storage().persistent().get(&key).ok_or(VestingError::ScheduleNotFound) + /// Return the total tokens already claimed by `beneficiary`. + pub fn get_claimed_amount(env: Env, beneficiary: Address) -> i128 { + env.storage() + .persistent() + .get(&VestingKey::Claimed(beneficiary)) + .unwrap_or(0_i128) } - /// Claimable amount for a schedule (vested minus already claimed). - /// Renamed to `get_claimable_vesting` to avoid symbol conflicts with other contracts. - pub fn get_claimable_vesting( - env: Env, - admin: Address, - schedule_index: u32, - ) -> Result { - let schedule = Self::get_schedule(env.clone(), admin, schedule_index)?; - let vested = Self::vested_amount(&env, &schedule); - Ok(vested.saturating_sub(schedule.claimed_amount)) + /// Return the tokens vested (but not necessarily claimed) at the current + /// ledger timestamp. + /// + /// Returns `None` if no schedule exists. + pub fn get_vested_amount(env: Env, beneficiary: Address) -> Option { + let schedule: VestingSchedule = env + .storage() + .persistent() + .get(&VestingKey::Schedule(beneficiary))?; + let now = env.ledger().timestamp(); + Some(vested_amount(&schedule, now)) } - /// Number of schedules created by an admin. - pub fn get_schedule_count(env: Env, admin: Address) -> u32 { - env.storage().persistent().get(&VestingDataKey::ScheduleCount(admin)).unwrap_or(0) + /// Return the currently claimable amount for `beneficiary`. + /// + /// Returns `None` if no schedule exists, `Some(0)` if nothing is claimable + /// yet. + pub fn get_claimable_amount(env: Env, beneficiary: Address) -> Option { + let schedule: VestingSchedule = env + .storage() + .persistent() + .get(&VestingKey::Schedule(beneficiary.clone()))?; + let claimed: i128 = env + .storage() + .persistent() + .get(&VestingKey::Claimed(beneficiary)) + .unwrap_or(0_i128); + let now = env.ledger().timestamp(); + Some(claimable_amount(&schedule, claimed, now)) } - /// Returns the current vesting event schema version. - pub fn get_event_schema_version(env: Env) -> u32 { - let _ = env; - VESTING_EVENT_SCHEMA_VERSION + /// Return all schedules for a batch of beneficiaries. + /// Useful for off-chain dashboards. + pub fn get_vesting_schedules( + env: Env, + beneficiaries: Vec
, + ) -> Vec> { + let mut out = Vec::new(&env); + for b in beneficiaries.iter() { + let s = env + .storage() + .persistent() + .get(&VestingKey::Schedule(b)); + out.push_back(s); + } + out } -} +} \ No newline at end of file