diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 0b8fbb3..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,24 +0,0 @@ -# Pull Request - -## Description -Brief description of changes made in this PR. - -## Type of Change -- [ ] Bug fix -- [ ] New feature -- [ ] Breaking change -- [ ] Documentation update - -## Changes Made -- List main changes here - -## Testing -- [ ] Tests pass locally -- [ ] New tests added (if applicable) - -## Checklist -- [ ] Code follows project style guidelines -- [ ] Self-review completed -- [ ] Comments added for complex code -- [ ] Documentation updated -- [ ] No new warnings generated diff --git a/Cargo.lock b/Cargo.lock index 75b1290..7830860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,9 +152,9 @@ dependencies = [ [[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" @@ -189,11 +189,20 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[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 = "bytes-lit" @@ -204,14 +213,14 @@ dependencies = [ "num-bigint", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "cc" -version = "1.2.61" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" dependencies = [ "find-msvc-tools", "shlex", @@ -225,9 +234,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "num-traits", @@ -307,7 +316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -334,7 +343,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -368,7 +377,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -381,7 +390,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -392,7 +401,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -403,7 +412,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -428,7 +437,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -451,7 +459,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -518,9 +526,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 = "elliptic-curve" @@ -588,21 +596,21 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-core", "futures-task", @@ -662,9 +670,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hex" @@ -738,7 +746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -766,13 +774,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.97" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -799,9 +806,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libm" @@ -813,26 +820,27 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" name = "liquidity-pool-contract" version = "1.0.0" dependencies = [ + "parameters-contract", "soroban-sdk", ] [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -840,9 +848,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-derive" @@ -852,7 +860,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -942,7 +950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -956,27 +964,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha", @@ -1004,28 +1012,29 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "reputation-contract" version = "1.0.0" dependencies = [ + "parameters-contract", "soroban-sdk", ] @@ -1050,9 +1059,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "schemars" @@ -1099,9 +1108,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1109,29 +1118,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -1142,11 +1151,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64 0.22.1", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -1161,14 +1171,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1194,9 +1204,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signature" @@ -1216,9 +1226,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "soroban-builtin-sdk-macros" @@ -1229,7 +1239,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1309,7 +1319,7 @@ dependencies = [ "serde", "serde_json", "stellar-xdr", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1365,7 +1375,7 @@ dependencies = [ "soroban-spec", "soroban-spec-rust", "stellar-xdr", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1392,7 +1402,7 @@ dependencies = [ "sha2", "soroban-spec", "stellar-xdr", - "syn 2.0.117", + "syn 2.0.119", "thiserror", ] @@ -1411,9 +1421,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spki" @@ -1483,9 +1493,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -1509,17 +1530,16 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "time" -version = "0.3.47" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -1529,25 +1549,40 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicode-ident" @@ -1583,9 +1618,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -1596,9 +1631,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1606,22 +1641,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -1684,7 +1719,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1695,7 +1730,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1724,46 +1759,46 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/contracts/creditline-contract/src/errors.rs b/contracts/creditline-contract/src/errors.rs index 6125165..a65eb78 100644 --- a/contracts/creditline-contract/src/errors.rs +++ b/contracts/creditline-contract/src/errors.rs @@ -32,4 +32,5 @@ pub enum CreditLineError { InvalidLoanStatus = 25, NotInitialized = 26, InvalidDueDate = 27, + Paused = 28, } diff --git a/contracts/creditline-contract/src/lib.rs b/contracts/creditline-contract/src/lib.rs index 665d2d2..4dfd442 100644 --- a/contracts/creditline-contract/src/lib.rs +++ b/contracts/creditline-contract/src/lib.rs @@ -80,6 +80,7 @@ impl CreditLineContract { loan_type: LoanType, ) -> Result { user.require_auth(); + Self::require_not_paused(&env)?; Self::validate_guarantee(&env, total_amount, guarantee_amount)?; Self::validate_vendor(&env, &vendor)?; @@ -132,6 +133,7 @@ impl CreditLineContract { loan_type: LoanType, ) -> Result { user.require_auth(); + Self::require_not_paused(&env)?; Self::validate_guarantee(&env, total_amount, guarantee_amount)?; let score = Self::validate_reputation(&env, &user)?; @@ -190,6 +192,7 @@ impl CreditLineContract { let old_admin = storage::get_admin(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); old_admin.require_auth(); access::require_admin(&env, &old_admin); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); storage::set_admin(&env, &new_admin); } @@ -198,6 +201,7 @@ impl CreditLineContract { pub fn upgrade(env: Env, new_wasm_hash: soroban_sdk::BytesN<32>) { let admin = storage::get_admin(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); admin.require_auth(); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); // bump stored version and emit event let old_version = storage::get_version(&env).unwrap_or(1u32); @@ -214,24 +218,28 @@ impl CreditLineContract { pub fn set_reputation_contract(env: Env, admin: Address, address: Address) { admin.require_auth(); access::require_admin(&env, &admin); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); storage::set_reputation_contract(&env, &address); } pub fn set_vendor_registry(env: Env, admin: Address, address: Address) { admin.require_auth(); access::require_admin(&env, &admin); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); storage::set_vendor_registry(&env, &address); } pub fn set_liquidity_pool(env: Env, admin: Address, address: Address) { admin.require_auth(); access::require_admin(&env, &admin); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); storage::set_liquidity_pool(&env, &address); } pub fn set_parameters_contract(env: Env, admin: Address, address: Address) { admin.require_auth(); access::require_admin(&env, &admin); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); storage::set_parameters_contract(&env, &address); } @@ -492,6 +500,7 @@ impl CreditLineContract { } pub fn mark_defaulted(env: Env, loan_id: u64) -> Result<(), CreditLineError> { + Self::require_not_paused(&env)?; let mut loan = storage::read_loan(&env, loan_id)?; if loan.status != LoanStatus::Active { @@ -578,6 +587,7 @@ impl CreditLineContract { // 1. Admin auth - must be first let admin = storage::get_admin(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); admin.require_auth(); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); // 2. Load loan — panic if not found let mut loan = @@ -612,6 +622,7 @@ impl CreditLineContract { pub fn cancel_loan(env: Env, caller: Address, loan_id: u64) { caller.require_auth(); + Self::require_not_paused(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); let mut loan = storage::read_loan(&env, loan_id).unwrap_or_else(|err| panic_with_error!(&env, err)); @@ -647,6 +658,7 @@ impl CreditLineContract { amount: i128, ) -> Result { borrower.require_auth(); + Self::require_not_paused(&env)?; let mut loan = storage::read_loan(&env, loan_id)?; @@ -765,6 +777,7 @@ impl CreditLineContract { amount: i128, ) -> Result { borrower.require_auth(); + Self::require_not_paused(&env)?; let mut loan = storage::read_loan(&env, loan_id)?; @@ -966,6 +979,7 @@ impl CreditLineContract { /// `LOANLTFE` event when fees are accrued; is a no-op when no full day has /// elapsed since the last accrual or when no installment is overdue. pub fn apply_late_fees(env: Env, loan_id: u64) -> Result<(), CreditLineError> { + Self::require_not_paused(&env)?; let mut loan = storage::read_loan(&env, loan_id)?; if loan.status != LoanStatus::Active { @@ -1007,6 +1021,25 @@ impl CreditLineContract { ); } + fn require_not_paused(env: &Env) -> Result<(), CreditLineError> { + match storage::get_parameters_contract(env) + .unwrap_or_else(|err| panic_with_error!(env, err)) + { + Some(address) => { + let paused: bool = env.invoke_contract( + &address, + &Symbol::new(env, "is_paused"), + ().into_val(env), + ); + if paused { + return Err(CreditLineError::Paused); + } + Ok(()) + } + None => Ok(()), + } + } + fn get_protocol_parameters(env: &Env) -> ProtocolParameters { match storage::get_parameters_contract(env) .unwrap_or_else(|err| panic_with_error!(env, err)) diff --git a/contracts/liquidity-pool-contract/Cargo.toml b/contracts/liquidity-pool-contract/Cargo.toml index 2880292..a9fc262 100644 --- a/contracts/liquidity-pool-contract/Cargo.toml +++ b/contracts/liquidity-pool-contract/Cargo.toml @@ -11,4 +11,5 @@ soroban-sdk = "22.0.0" [dev-dependencies] soroban-sdk = { version = "22.0.0", features = ["testutils"] } +parameters-contract = { path = "../parameters-contract" } diff --git a/contracts/liquidity-pool-contract/src/errors.rs b/contracts/liquidity-pool-contract/src/errors.rs index 219f0d5..587a182 100644 --- a/contracts/liquidity-pool-contract/src/errors.rs +++ b/contracts/liquidity-pool-contract/src/errors.rs @@ -15,4 +15,5 @@ pub enum LiquidityPoolError { NotCreditLine = 9, ZeroTotalShares = 10, ReentrancyDetected = 11, + Paused = 12, } diff --git a/contracts/liquidity-pool-contract/src/lib.rs b/contracts/liquidity-pool-contract/src/lib.rs index 6ac975b..a1118b6 100644 --- a/contracts/liquidity-pool-contract/src/lib.rs +++ b/contracts/liquidity-pool-contract/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -use soroban_sdk::{contract, contractimpl, panic_with_error, token, Address, Env}; +use soroban_sdk::{contract, contractimpl, panic_with_error, token, Address, Env, IntoVal, Symbol}; mod errors; mod events; @@ -50,25 +50,36 @@ impl LiquidityPoolContract { pub fn set_creditline(env: Env, admin: Address, creditline: Address) { admin.require_auth(); Self::require_admin(&env, &admin); + Self::require_not_paused(&env); storage::set_creditline(&env, &creditline); } pub fn set_treasury(env: Env, admin: Address, treasury: Address) { admin.require_auth(); Self::require_admin(&env, &admin); + Self::require_not_paused(&env); storage::set_treasury(&env, &treasury); } pub fn set_merchant_fund(env: Env, admin: Address, merchant_fund: Address) { admin.require_auth(); Self::require_admin(&env, &admin); + Self::require_not_paused(&env); storage::set_merchant_fund(&env, &merchant_fund); } + pub fn set_parameters_contract(env: Env, admin: Address, address: Address) { + admin.require_auth(); + Self::require_admin(&env, &admin); + Self::require_not_paused(&env); + storage::set_parameters_contract(&env, &address); + } + pub fn set_admin(env: Env, new_admin: Address) { let old_admin = storage::get_admin(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); old_admin.require_auth(); Self::require_admin(&env, &old_admin); + Self::require_not_paused(&env); storage::set_admin(&env, &new_admin); } @@ -76,6 +87,7 @@ impl LiquidityPoolContract { pub fn upgrade(env: Env, new_wasm_hash: soroban_sdk::BytesN<32>) { let admin = storage::get_admin(&env).unwrap_or_else(|err| panic_with_error!(&env, err)); admin.require_auth(); + Self::require_not_paused(&env); // bump and persist version let old_version = storage::get_version(&env).unwrap_or(1u32); let new_version = old_version.checked_add(1).unwrap_or(old_version); @@ -106,6 +118,7 @@ impl LiquidityPoolContract { /// Returns the number of shares issued. pub fn deposit(env: Env, provider: Address, amount: i128) -> Result { provider.require_auth(); + Self::require_not_paused(&env); if amount < types::MIN_AMOUNT { return Err(LiquidityPoolError::InvalidAmount); @@ -160,6 +173,7 @@ impl LiquidityPoolContract { /// Returns the number of tokens returned. pub fn withdraw(env: Env, provider: Address, shares: i128) -> Result { provider.require_auth(); + Self::require_not_paused(&env); if shares < types::MIN_AMOUNT { return Err(LiquidityPoolError::InvalidAmount); @@ -225,6 +239,7 @@ impl LiquidityPoolContract { ) -> Result<(), LiquidityPoolError> { creditline.require_auth(); Self::require_creditline(&env, &creditline); + Self::require_not_paused(&env); if amount <= 0 { return Err(LiquidityPoolError::InvalidAmount); @@ -267,6 +282,7 @@ impl LiquidityPoolContract { ) -> Result<(), LiquidityPoolError> { creditline.require_auth(); Self::require_creditline(&env, &creditline); + Self::require_not_paused(&env); if principal < 0 || interest < 0 { return Err(LiquidityPoolError::InvalidAmount); @@ -309,6 +325,7 @@ impl LiquidityPoolContract { ) -> Result<(), LiquidityPoolError> { creditline.require_auth(); Self::require_creditline(&env, &creditline); + Self::require_not_paused(&env); if amount <= 0 { return Err(LiquidityPoolError::InvalidAmount); @@ -554,6 +571,7 @@ impl LiquidityPoolContract { } /// Calculate how many tokens `shares` are worth at the current share price. + /// Returns 0 if the pool is empty (no shares issued). pub fn calculate_withdrawal(env: Env, shares: i128) -> i128 { if shares == 0 { return 0; @@ -562,7 +580,8 @@ impl LiquidityPoolContract { if total_shares == 0 { return 0; } - let share_price = Self::calculate_share_price_internal(&env).unwrap_or(types::SHARE_PRICE_PRECISION); + let share_price = Self::calculate_share_price_internal(&env) + .unwrap_or(types::SHARE_PRICE_PRECISION); safe_math::div_i128( safe_math::mul_i128(shares, share_price).unwrap_or(0), types::SHARE_PRICE_PRECISION, @@ -593,6 +612,24 @@ impl LiquidityPoolContract { } } + fn require_not_paused(env: &Env) { + match storage::get_parameters_contract(env) + .unwrap_or_else(|err| panic_with_error!(env, err)) + { + Some(address) => { + let paused: bool = env.invoke_contract( + &address, + &Symbol::new(env, "is_paused"), + ().into_val(env), + ); + if paused { + panic_with_error!(env, LiquidityPoolError::Paused); + } + } + None => (), + } + } + fn require_creditline(env: &Env, caller: &Address) { let creditline = storage::get_creditline(env) .unwrap_or_else(|err| panic_with_error!(env, err)) diff --git a/contracts/liquidity-pool-contract/src/storage.rs b/contracts/liquidity-pool-contract/src/storage.rs index 5841059..abe17b9 100644 --- a/contracts/liquidity-pool-contract/src/storage.rs +++ b/contracts/liquidity-pool-contract/src/storage.rs @@ -19,6 +19,7 @@ pub const PERSISTENT_TTL_THRESHOLD: u32 = 1_036_800; pub const PERSISTENT_TTL_EXTEND_TO: u32 = 2_073_600; // Version key (instance storage) pub const VERSION_KEY: Symbol = symbol_short!("VERSION"); +pub const PARAMETERS_KEY: Symbol = symbol_short!("PARAMS"); // --- Admin --- @@ -157,3 +158,11 @@ pub fn get_version(env: &Env) -> Result { pub fn set_version(env: &Env, v: u32) { env.storage().instance().set(&VERSION_KEY, &v); } + +pub fn get_parameters_contract(env: &Env) -> Result, LiquidityPoolError> { + Ok(env.storage().instance().get(&PARAMETERS_KEY)) +} + +pub fn set_parameters_contract(env: &Env, address: &Address) { + env.storage().instance().set(&PARAMETERS_KEY, address); +} diff --git a/contracts/liquidity-pool-contract/src/tests.rs b/contracts/liquidity-pool-contract/src/tests.rs index ad1c26d..92ef425 100644 --- a/contracts/liquidity-pool-contract/src/tests.rs +++ b/contracts/liquidity-pool-contract/src/tests.rs @@ -105,6 +105,59 @@ fn test_initialize_twice_fails() { ); } +// ─── pause ───────────────────────────────────────────────────────────────────── + +#[test] +fn test_pause_defaults_to_false() { + let t = TestEnv::setup(); + let params_id = t.env.register(parameters_contract::ParametersContract, ()); + let params = parameters_contract::ParametersContractClient::new(&t.env, ¶ms_id); + params.initialize_defaults(&t.admin); + t.client.set_parameters_contract(&t.admin, ¶ms_id); + + assert!(!params.is_paused()); +} + +#[test] +fn test_mutating_functions_fail_while_paused() { + let t = TestEnv::setup(); + let params_id = t.env.register(parameters_contract::ParametersContract, ()); + let params = parameters_contract::ParametersContractClient::new(&t.env, ¶ms_id); + params.initialize_defaults(&t.admin); + t.client.set_parameters_contract(&t.admin, ¶ms_id); + + // Pause via params contract + params.set_paused(&t.admin, &true); + + let provider = Address::generate(&t.env); + t.mint(&provider, 1_000); + + // deposit should fail + assert_eq!( + t.client.try_deposit(&provider, &1_000), + Err(Ok(LiquidityPoolError::Paused)) + ); +} + +#[test] +fn test_read_only_functions_continue_while_paused() { + let t = TestEnv::setup(); + let params_id = t.env.register(parameters_contract::ParametersContract, ()); + let params = parameters_contract::ParametersContractClient::new(&t.env, ¶ms_id); + params.initialize_defaults(&t.admin); + t.client.set_parameters_contract(&t.admin, ¶ms_id); + + // Pause + params.set_paused(&t.admin, &true); + + // get_version should work + assert_eq!(t.client.get_version(), 1u32); + + // get_pool_stats should work (read-only) + let stats = t.client.get_pool_stats(); + assert_eq!(stats.total_liquidity, 0); +} + // ─── deposit ────────────────────────────────────────────────────────────────── #[test] diff --git a/contracts/parameters-contract/src/errors.rs b/contracts/parameters-contract/src/errors.rs index ce27739..a10ce4e 100644 --- a/contracts/parameters-contract/src/errors.rs +++ b/contracts/parameters-contract/src/errors.rs @@ -21,4 +21,5 @@ pub enum ParametersError { ProposalAlreadyExecuted = 15, DuplicateSignature = 16, ThresholdNotMet = 17, + Paused = 18, } diff --git a/contracts/parameters-contract/src/lib.rs b/contracts/parameters-contract/src/lib.rs index 837b6bc..44e7eb0 100644 --- a/contracts/parameters-contract/src/lib.rs +++ b/contracts/parameters-contract/src/lib.rs @@ -139,6 +139,7 @@ impl ParametersContract { ProposalAction::SetAdmin(a) => Self::do_set_admin(&env, &a), ProposalAction::Upgrade(h) => Self::do_upgrade(&env, h), ProposalAction::UpdateSigners(c) => Self::do_update_signers(&env, &c), + ProposalAction::SetPaused(paused) => storage::set_paused(&env, paused), } proposal.executed = true; @@ -164,6 +165,16 @@ impl ParametersContract { storage::get_parameters(&env) } + pub fn set_paused(env: Env, admin: Address, paused: bool) { + admin.require_auth(); + access::require_admin(&env, &admin); + storage::set_paused(&env, paused); + } + + pub fn is_paused(env: Env) -> bool { + storage::is_paused(&env) + } + fn do_update_parameters(env: &Env, params: &ProtocolParameters) { Self::validate_parameters(env, params); diff --git a/contracts/parameters-contract/src/storage.rs b/contracts/parameters-contract/src/storage.rs index 6f2c3e6..99f6fe3 100644 --- a/contracts/parameters-contract/src/storage.rs +++ b/contracts/parameters-contract/src/storage.rs @@ -9,6 +9,7 @@ pub const REENTRANCY_LOCK: Symbol = symbol_short!("LOCKED"); pub const VERSION_KEY: Symbol = symbol_short!("VERSION"); pub const MULTISIG_KEY: Symbol = symbol_short!("MSIG"); pub const PROP_CNT_KEY: Symbol = symbol_short!("PROPCNT"); +pub const PAUSED_KEY: Symbol = symbol_short!("PAUSED"); // TTL constants (in ledgers — 1 ledger ≈ 5 seconds on mainnet) pub const PERSISTENT_TTL_THRESHOLD: u32 = 1_036_800; // 60 days @@ -98,3 +99,11 @@ pub fn set_proposal(env: &Env, proposal: &Proposal) { .persistent() .extend_ttl(&key, PERSISTENT_TTL_THRESHOLD, PERSISTENT_TTL_EXTEND_TO); } + +pub fn set_paused(env: &Env, paused: bool) { + env.storage().instance().set(&PAUSED_KEY, &paused); +} + +pub fn is_paused(env: &Env) -> bool { + env.storage().instance().get(&PAUSED_KEY).unwrap_or(false) +} diff --git a/contracts/parameters-contract/src/tests.rs b/contracts/parameters-contract/src/tests.rs index 41fd4ed..cf74c62 100644 --- a/contracts/parameters-contract/src/tests.rs +++ b/contracts/parameters-contract/src/tests.rs @@ -285,6 +285,53 @@ fn test_upgrade_via_proposal_increments_version() { assert!(found, "CONTRACTUPGRADED event not found"); } +#[test] +fn test_pause_defaults_to_false() { + let (_env, client, admin) = setup(); + client.initialize_defaults(&admin); + assert!(!client.is_paused()); +} + +#[test] +fn test_admin_can_pause() { + let (_env, client, admin) = setup(); + client.initialize_defaults(&admin); + client.set_paused(&admin, &true); + assert!(client.is_paused()); +} + +#[test] +fn test_admin_can_unpause() { + let (_env, client, admin) = setup(); + client.initialize_defaults(&admin); + client.set_paused(&admin, &true); + assert!(client.is_paused()); + client.set_paused(&admin, &false); + assert!(!client.is_paused()); +} + +#[test] +#[should_panic(expected = "Error(Contract, #2)")] // NotAdmin +fn test_unauthorized_caller_cannot_pause() { + let (env, client, admin) = setup(); + client.initialize_defaults(&admin); + let intruder = Address::generate(&env); + client.set_paused(&intruder, &true); +} + +#[test] +fn test_set_paused_via_proposal() { + let (env, client, _admin, s1, s2, _s3) = setup_multisig(); + + assert!(!client.is_paused()); + + let id = client.propose(&s1, &ProposalAction::SetPaused(true)); + client.approve(&s2, &id); + client.execute(&id); + + assert!(client.is_paused()); +} + #[test] fn test_three_of_three_with_full_committee_approval() { let (env, client, admin) = setup(); diff --git a/contracts/parameters-contract/src/types.rs b/contracts/parameters-contract/src/types.rs index 8d42d23..f11dcea 100644 --- a/contracts/parameters-contract/src/types.rs +++ b/contracts/parameters-contract/src/types.rs @@ -54,6 +54,7 @@ pub enum ProposalAction { SetAdmin(Address), Upgrade(BytesN<32>), UpdateSigners(MultisigConfig), + SetPaused(bool), } #[contracttype] diff --git a/contracts/reputation-contract/Cargo.toml b/contracts/reputation-contract/Cargo.toml index 446cc32..8f3d5ed 100644 --- a/contracts/reputation-contract/Cargo.toml +++ b/contracts/reputation-contract/Cargo.toml @@ -18,6 +18,7 @@ soroban-sdk = "22.0.0" [dev-dependencies] soroban-sdk = { version = "22.0.0", features = ["testutils"] } +parameters-contract = { path = "../parameters-contract" } # OpenZeppelin Testing Helpers (optional) # soroban-test-helpers = { git = "https://github.com/OpenZeppelin/soroban-helpers", branch = "main" } diff --git a/contracts/reputation-contract/src/errors.rs b/contracts/reputation-contract/src/errors.rs index 9685212..df395dc 100644 --- a/contracts/reputation-contract/src/errors.rs +++ b/contracts/reputation-contract/src/errors.rs @@ -12,4 +12,5 @@ pub enum ReputationError { Underflow = 5, NotInitialized = 6, ReentrancyDetected = 7, + Paused = 8, } diff --git a/contracts/reputation-contract/src/lib.rs b/contracts/reputation-contract/src/lib.rs index e125889..7eff8a5 100644 --- a/contracts/reputation-contract/src/lib.rs +++ b/contracts/reputation-contract/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env}; +use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, IntoVal, Symbol}; // Module imports mod access; @@ -40,6 +40,7 @@ impl ReputationContract { ) -> Result<(), ReputationError> { updater.require_auth(); access::require_updater(&env, &updater); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); @@ -70,6 +71,7 @@ impl ReputationContract { ) -> Result<(), ReputationError> { updater.require_auth(); access::require_updater(&env, &updater); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); @@ -91,6 +93,7 @@ impl ReputationContract { pub fn set_score(env: Env, updater: Address, user: Address, new_score: u32) { updater.require_auth(); access::require_updater(&env, &updater); + Self::require_not_paused(&env); if new_score > types::MAX_SCORE { soroban_sdk::panic_with_error!(&env, ReputationError::OutOfBounds); @@ -118,6 +121,7 @@ impl ReputationContract { ) -> Result<(), ReputationError> { updater.require_auth(); access::require_updater(&env, &updater); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); @@ -148,6 +152,7 @@ impl ReputationContract { ) -> Result<(), ReputationError> { updater.require_auth(); access::require_updater(&env, &updater); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); @@ -169,6 +174,7 @@ impl ReputationContract { pub fn set_updater(env: Env, admin: Address, updater: Address, allowed: bool) { admin.require_auth(); access::require_admin(&env, &admin); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); @@ -186,6 +192,13 @@ impl ReputationContract { /// Set the admin address for this contract /// Requires authorization from current admin (or allows initial setup) + pub fn set_parameters_contract(env: Env, admin: Address, address: Address) { + admin.require_auth(); + access::require_admin(&env, &admin); + Self::require_not_paused(&env); + storage::set_parameters_contract(&env, &address); + } + pub fn set_admin(env: Env, new_admin: Address) { let old_admin_opt: Option
= env.storage().instance().get(&storage::ADMIN_KEY); @@ -193,6 +206,7 @@ impl ReputationContract { // Admin exists, require current admin authorization old_admin.require_auth(); access::require_admin(&env, &old_admin); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); @@ -213,6 +227,7 @@ impl ReputationContract { let admin = storage::get_admin(&env) .unwrap_or_else(|err| soroban_sdk::panic_with_error!(&env, err)); admin.require_auth(); + Self::require_not_paused(&env); Self::enter_non_reentrant(&env); let old = storage::get_version(&env).unwrap_or(1u32); @@ -226,6 +241,24 @@ impl ReputationContract { storage::get_admin(&env) } + fn require_not_paused(env: &Env) { + let params_contract = match storage::get_parameters_contract(env) + .unwrap_or_else(|err| soroban_sdk::panic_with_error!(env, err)) + { + Some(address) => address, + None => return, + }; + + let paused: bool = env.invoke_contract( + ¶ms_contract, + &Symbol::new(env, "is_paused"), + ().into_val(env), + ); + if paused { + soroban_sdk::panic_with_error!(env, ReputationError::Paused); + } + } + fn enter_non_reentrant(env: &Env) { if storage::is_reentrancy_locked(env) .unwrap_or_else(|err| soroban_sdk::panic_with_error!(env, err)) diff --git a/contracts/reputation-contract/src/storage.rs b/contracts/reputation-contract/src/storage.rs index c099f44..2c264e6 100644 --- a/contracts/reputation-contract/src/storage.rs +++ b/contracts/reputation-contract/src/storage.rs @@ -8,6 +8,7 @@ pub const UPDATERS_MAP: Symbol = symbol_short!("UPDATERS"); pub const SCORES_MAP: Symbol = symbol_short!("SCORES"); pub const REENTRANCY_LOCK: Symbol = symbol_short!("LOCKED"); pub const VERSION_KEY: Symbol = symbol_short!("VERSION"); +pub const PARAMETERS_KEY: Symbol = symbol_short!("PARAMS"); /// Get the admin address from storage pub fn get_admin(env: &Env) -> Result { @@ -92,3 +93,11 @@ pub fn get_version(env: &Env) -> Result { pub fn set_version(env: &Env, v: u32) { env.storage().instance().set(&VERSION_KEY, &v); } + +pub fn get_parameters_contract(env: &Env) -> Result, ReputationError> { + Ok(env.storage().instance().get(&PARAMETERS_KEY)) +} + +pub fn set_parameters_contract(env: &Env, address: &Address) { + env.storage().instance().set(&PARAMETERS_KEY, address); +} diff --git a/contracts/reputation-contract/src/tests.rs b/contracts/reputation-contract/src/tests.rs index 8a9b64d..a9b3f78 100644 --- a/contracts/reputation-contract/src/tests.rs +++ b/contracts/reputation-contract/src/tests.rs @@ -52,6 +52,73 @@ fn it_gets_admin() { assert_eq!(retrieved, admin); } +// ─── pause ───────────────────────────────────────────────────────────────────── + +struct PauseTestCtx { + env: Env, + client: ReputationContractClient<'static>, + admin: Address, + params: parameters_contract::ParametersContractClient<'static>, +} + +fn setup_pause() -> PauseTestCtx { + let env = Env::default(); + env.mock_all_auths(); + + let contract_id = env.register(ReputationContract, ()); + let client = ReputationContractClient::new(&env, &contract_id); + let client: ReputationContractClient<'static> = unsafe { core::mem::transmute(client) }; + let admin = Address::generate(&env); + client.set_admin(&admin); + client.set_updater(&admin, &admin, &true); + + let params_id = env.register(parameters_contract::ParametersContract, ()); + let params = parameters_contract::ParametersContractClient::new(&env, ¶ms_id); + let params: parameters_contract::ParametersContractClient<'static> = + unsafe { core::mem::transmute(params) }; + params.initialize_defaults(&admin); + client.set_parameters_contract(&admin, ¶ms_id); + + PauseTestCtx { + env, + client, + admin, + params, + } +} + +#[test] +fn test_mutating_functions_fail_while_paused() { + let ctx = setup_pause(); + + // Pause + ctx.params.set_paused(&ctx.admin, &true); + + let user = Address::generate(&ctx.env); + + // increase_score should fail + assert_eq!( + ctx.client + .try_increase_score(&ctx.admin, &user, &10u32), + Err(Ok(ReputationError::Paused)) + ); +} + +#[test] +fn test_read_only_functions_continue_while_paused() { + let ctx = setup_pause(); + + // Pause + ctx.params.set_paused(&ctx.admin, &true); + + // get_score should work (read-only) + let user = Address::generate(&ctx.env); + assert_eq!(ctx.client.get_score(&user), 0u32); + + // get_version should work + assert_eq!(ctx.client.get_version(), 1u32); +} + /// Test: Assigns updater permissions #[test] fn it_sets_updater() {