diff --git a/.gitignore b/.gitignore
index 1b0d5f5..bf8ace5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
dongle-smartcontract/target/
+dongle-smartcontract/test_snapshots/
diff --git a/dongle-smartcontract/.gitignore b/dongle-smartcontract/.gitignore
index 9f97022..a4f7f77 100644
--- a/dongle-smartcontract/.gitignore
+++ b/dongle-smartcontract/.gitignore
@@ -1 +1,2 @@
-target/
\ No newline at end of file
+target/
+test_snapshots/
\ No newline at end of file
diff --git a/dongle-smartcontract/Cargo.lock b/dongle-smartcontract/Cargo.lock
index 9e8306e..e23ef9f 100644
--- a/dongle-smartcontract/Cargo.lock
+++ b/dongle-smartcontract/Cargo.lock
@@ -3,20 +3,17 @@
version = 4
[[package]]
-name = "addr2line"
-version = "0.25.1"
+name = "ahash"
+version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
- "gimli",
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
]
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -36,43 +33,145 @@ dependencies = [
]
[[package]]
-name = "autocfg"
-version = "1.5.0"
+name = "ark-bls12-381"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+]
[[package]]
-name = "backtrace"
-version = "0.3.76"
+name = "ark-bn254"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
+checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f"
dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-link",
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
]
[[package]]
-name = "base16ct"
-version = "0.2.0"
+name = "ark-ec"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba"
+dependencies = [
+ "ark-ff",
+ "ark-poly",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "hashbrown 0.13.2",
+ "itertools",
+ "num-traits",
+ "zeroize",
+]
+
+[[package]]
+name = "ark-ff"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba"
+dependencies = [
+ "ark-ff-asm",
+ "ark-ff-macros",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "digest",
+ "itertools",
+ "num-bigint",
+ "num-traits",
+ "paste",
+ "rustc_version",
+ "zeroize",
+]
[[package]]
-name = "base32"
+name = "ark-ff-asm"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "ark-ff-macros"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565"
+dependencies = [
+ "num-bigint",
+ "num-traits",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "ark-poly"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf"
+dependencies = [
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "hashbrown 0.13.2",
+]
+
+[[package]]
+name = "ark-serialize"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5"
+dependencies = [
+ "ark-serialize-derive",
+ "ark-std",
+ "digest",
+ "num-bigint",
+]
+
+[[package]]
+name = "ark-serialize-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "ark-std"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
+checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185"
+dependencies = [
+ "num-traits",
+ "rand",
+]
[[package]]
-name = "base64"
-version = "0.13.1"
+name = "autocfg"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
@@ -101,6 +200,12 @@ 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"
@@ -110,7 +215,7 @@ dependencies = [
"num-bigint",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -129,6 +234,17 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+[[package]]
+name = "cfg_eval"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "chrono"
version = "0.4.44"
@@ -197,14 +313,20 @@ dependencies = [
[[package]]
name = "ctor"
-version = "0.2.9"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
+checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb"
dependencies = [
- "quote",
- "syn",
+ "ctor-proc-macro",
+ "dtor",
]
+[[package]]
+name = "ctor-proc-macro"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2"
+
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
@@ -229,7 +351,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -263,7 +385,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -277,7 +399,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -288,7 +410,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core 0.20.11",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -299,9 +421,15 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
dependencies = [
"darling_core 0.21.3",
"quote",
- "syn",
+ "syn 2.0.117",
]
+[[package]]
+name = "data-encoding"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
+
[[package]]
name = "der"
version = "0.7.10"
@@ -322,6 +450,17 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
@@ -330,7 +469,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -358,6 +497,21 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+[[package]]
+name = "dtor"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301"
+dependencies = [
+ "dtor-proc-macro",
+]
+
+[[package]]
+name = "dtor-proc-macro"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
+
[[package]]
name = "dyn-clone"
version = "1.0.20"
@@ -496,12 +650,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "gimli"
-version = "0.32.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
-
[[package]]
name = "group"
version = "0.13.0"
@@ -513,18 +661,52 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "hash32"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
+dependencies = [
+ "byteorder",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+[[package]]
+name = "heapless"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
+dependencies = [
+ "hash32",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "hex"
version = "0.4.3"
@@ -610,9 +792,9 @@ checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590"
[[package]]
name = "itertools"
-version = "0.11.0"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
@@ -673,19 +855,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
-name = "memchr"
-version = "2.8.0"
+name = "macro-string"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
[[package]]
-name = "miniz_oxide"
-version = "0.8.9"
+name = "memchr"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
-dependencies = [
- "adler2",
-]
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "num-bigint"
@@ -711,7 +895,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -732,15 +916,6 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "object"
-version = "0.37.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -797,7 +972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -874,7 +1049,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -887,12 +1062,6 @@ dependencies = [
"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"
@@ -908,6 +1077,17 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+[[package]]
+name = "schemars"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
+dependencies = [
+ "dyn-clone",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "schemars"
version = "0.9.0"
@@ -978,7 +1158,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -1000,11 +1180,12 @@ version = "3.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7"
dependencies = [
- "base64 0.22.1",
+ "base64",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.13.0",
+ "schemars 0.8.22",
"schemars 0.9.0",
"schemars 1.2.1",
"serde_core",
@@ -1022,7 +1203,7 @@ dependencies = [
"darling 0.21.3",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -1070,21 +1251,21 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "soroban-builtin-sdk-macros"
-version = "21.2.1"
+version = "25.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f57a68ef8777e28e274de0f3a88ad9a5a41d9a2eb461b4dd800b086f0e83b80"
+checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5"
dependencies = [
"itertools",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
name = "soroban-env-common"
-version = "21.2.1"
+version = "25.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd1c89463835fe6da996318156d39f424b4f167c725ec692e5a7a2d4e694b3d"
+checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01"
dependencies = [
"arbitrary",
"crate-git-revision",
@@ -1101,9 +1282,9 @@ dependencies = [
[[package]]
name = "soroban-env-guest"
-version = "21.2.1"
+version = "25.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bfb2536811045d5cd0c656a324cbe9ce4467eb734c7946b74410d90dea5d0ce"
+checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55"
dependencies = [
"soroban-env-common",
"static_assertions",
@@ -1111,11 +1292,15 @@ dependencies = [
[[package]]
name = "soroban-env-host"
-version = "21.2.1"
+version = "25.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b7a32c28f281c423189f1298960194f0e0fc4eeb72378028171e556d8cd6160"
+checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec"
dependencies = [
- "backtrace",
+ "ark-bls12-381",
+ "ark-bn254",
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
"curve25519-dalek",
"ecdsa",
"ed25519-dalek",
@@ -1138,15 +1323,15 @@ dependencies = [
"soroban-env-common",
"soroban-wasmi",
"static_assertions",
- "stellar-strkey",
+ "stellar-strkey 0.0.13",
"wasmparser",
]
[[package]]
name = "soroban-env-macros"
-version = "21.2.1"
+version = "25.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "242926fe5e0d922f12d3796cd7cd02dd824e5ef1caa088f45fce20b618309f64"
+checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c"
dependencies = [
"itertools",
"proc-macro2",
@@ -1154,14 +1339,14 @@ dependencies = [
"serde",
"serde_json",
"stellar-xdr",
- "syn",
+ "syn 2.0.117",
]
[[package]]
name = "soroban-ledger-snapshot"
-version = "21.7.7"
+version = "25.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6edf92749fd8399b417192d301c11f710b9cdce15789a3d157785ea971576fa"
+checksum = "66d569a1315f05216d024653ad87541aa15d3ff26dad9f8a98719cb53ccf2bf3"
dependencies = [
"serde",
"serde_json",
@@ -1173,12 +1358,13 @@ dependencies = [
[[package]]
name = "soroban-sdk"
-version = "21.7.7"
+version = "25.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dcdf04484af7cc731a7a48ad1d9f5f940370edeea84734434ceaf398a6b862e"
+checksum = "add8d19cfd2c9941bbdc7c8223c3cf9d7ff9af4554ba3bd4ae93e16b19b08aea"
dependencies = [
"arbitrary",
"bytes-lit",
+ "crate-git-revision",
"ctor",
"derive_arbitrary",
"ed25519-dalek",
@@ -1190,36 +1376,37 @@ dependencies = [
"soroban-env-host",
"soroban-ledger-snapshot",
"soroban-sdk-macros",
- "stellar-strkey",
+ "stellar-strkey 0.0.16",
+ "visibility",
]
[[package]]
name = "soroban-sdk-macros"
-version = "21.7.7"
+version = "25.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0974e413731aeff2443f2305b344578b3f1ffd18335a7ba0f0b5d2eb4e94c9ce"
+checksum = "2a0107e34575ec704ce29407695462e79e6b0e13ce7af6431b2f15c313e34464"
dependencies = [
- "crate-git-revision",
"darling 0.20.11",
+ "heck",
"itertools",
+ "macro-string",
"proc-macro2",
"quote",
- "rustc_version",
"sha2",
"soroban-env-common",
"soroban-spec",
"soroban-spec-rust",
"stellar-xdr",
- "syn",
+ "syn 2.0.117",
]
[[package]]
name = "soroban-spec"
-version = "21.7.7"
+version = "25.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2c70b20e68cae3ef700b8fa3ae29db1c6a294b311fba66918f90cb8f9fd0a1a"
+checksum = "53a1c9f6ccc6aa78518545e3cf542bd26f11d9085328a2e1c06c90514733fe15"
dependencies = [
- "base64 0.13.1",
+ "base64",
"stellar-xdr",
"thiserror",
"wasmparser",
@@ -1227,9 +1414,9 @@ dependencies = [
[[package]]
name = "soroban-spec-rust"
-version = "21.7.7"
+version = "25.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2dafbde981b141b191c6c036abc86097070ddd6eaaa33b273701449501e43d3"
+checksum = "e8247d3c6256b544b2461606c6892351bb22978d751e07c1aea744377053d852"
dependencies = [
"prettyplease",
"proc-macro2",
@@ -1237,7 +1424,7 @@ dependencies = [
"sha2",
"soroban-spec",
"stellar-xdr",
- "syn",
+ "syn 2.0.117",
"thiserror",
]
@@ -1270,6 +1457,12 @@ dependencies = [
"der",
]
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -1278,29 +1471,42 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stellar-strkey"
-version = "0.0.8"
+version = "0.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd"
+checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913"
dependencies = [
- "base32",
"crate-git-revision",
- "thiserror",
+ "data-encoding",
+]
+
+[[package]]
+name = "stellar-strkey"
+version = "0.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520"
+dependencies = [
+ "crate-git-revision",
+ "data-encoding",
+ "heapless",
]
[[package]]
name = "stellar-xdr"
-version = "21.2.0"
+version = "25.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50"
+checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf"
dependencies = [
"arbitrary",
- "base64 0.13.1",
+ "base64",
+ "cfg_eval",
"crate-git-revision",
"escape-bytes",
+ "ethnum",
"hex",
"serde",
"serde_with",
- "stellar-strkey",
+ "sha2",
+ "stellar-strkey 0.0.13",
]
[[package]]
@@ -1315,6 +1521,17 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
[[package]]
name = "syn"
version = "2.0.117"
@@ -1343,7 +1560,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -1395,6 +1612,17 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+[[package]]
+name = "visibility"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
@@ -1433,7 +1661,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
"wasm-bindgen-shared",
]
@@ -1504,7 +1732,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -1515,7 +1743,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -1559,7 +1787,7 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.117",
]
[[package]]
@@ -1567,6 +1795,20 @@ name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
[[package]]
name = "zmij"
diff --git a/dongle-smartcontract/Cargo.toml b/dongle-smartcontract/Cargo.toml
index 5994004..e767a32 100644
--- a/dongle-smartcontract/Cargo.toml
+++ b/dongle-smartcontract/Cargo.toml
@@ -7,10 +7,13 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[dependencies]
-soroban-sdk = { version = "21.0.0", features = ["alloc"] }
+soroban-sdk = { version = "25.1.1", features = ["alloc"] }
[dev-dependencies]
-soroban-sdk = { version = "21.0.0", features = ["testutils", "alloc"] }
+soroban-sdk = { version = "25.1.1", features = ["testutils", "alloc"] }
+
+[features]
+testutils = ["soroban-sdk/testutils"]
[profile.release]
opt-level = "z"
@@ -25,10 +28,3 @@ lto = true
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
-
-[features]
-testutils = ["soroban-sdk/testutils"]
-
-[profile.release]
-opt-level = "z"
-overflow-checks = true
\ No newline at end of file
diff --git a/dongle-smartcontract/src/errors.rs b/dongle-smartcontract/src/errors.rs
index 93bb67c..474486d 100644
--- a/dongle-smartcontract/src/errors.rs
+++ b/dongle-smartcontract/src/errors.rs
@@ -8,23 +8,62 @@ use soroban_sdk::contracterror;
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ContractError {
- ProjectNotFound = 1,
- Unauthorized = 2,
- ProjectAlreadyExists = 3,
- InvalidRating = 4,
- ReviewNotFound = 5,
- VerificationNotFound = 6,
- InvalidStatusTransition = 7,
- AdminOnly = 8,
- InvalidFeeAmount = 9,
- InsufficientFee = 10,
- InvalidProjectData = 11,
- ProjectNameTooLong = 12,
- ProjectDescriptionTooLong = 13,
- InvalidProjectCategory = 14,
- VerificationAlreadyProcessed = 15,
- CannotReviewOwnProject = 16,
- FeeConfigNotSet = 17,
- TreasuryNotSet = 18,
- NotReviewer = 19,
+ // ---- Project registry (1xx) ----
+ /// Project not found.
+ ProjectNotFound = 101,
+ /// Caller is not the project owner; unauthorized update.
+ NotProjectOwner = 102,
+ /// Project name is empty or invalid.
+ InvalidProjectName = 103,
+ /// Project description is empty or invalid.
+ InvalidProjectDescription = 104,
+ /// Project category is empty or invalid.
+ InvalidProjectCategory = 105,
+ /// String length exceeds maximum allowed (name, description, category, website, CIDs).
+ StringLengthExceeded = 106,
+ /// User has reached the maximum number of projects they can register.
+ MaxProjectsPerUserExceeded = 107,
+ /// Project ID is invalid (e.g. zero).
+ InvalidProjectId = 108,
+
+ // ---- Review registry (2xx) ----
+ /// Rating must be between 1 and 5 (inclusive).
+ InvalidRating = 201,
+ /// Reviewer has already submitted a review for this project; use update instead.
+ DuplicateReview = 202,
+ /// No review found for this project and reviewer.
+ ReviewNotFound = 203,
+ /// Only the original reviewer can update or delete the review.
+ NotReviewAuthor = 204,
+ /// Cannot compute aggregates when there are zero reviews (e.g. average rating).
+ ZeroReviews = 205,
+ /// CID is present but invalid: wrong prefix or too short.
+ /// Valid CIDs start with "Qm" (CIDv0, 46 chars) or "bafy" (CIDv1).
+ InvalidCid = 206,
+
+ // ---- Verification registry (3xx) ----
+ /// Verification record not found for this project.
+ VerificationNotFound = 301,
+ /// Caller is not the project owner; only owner can request verification.
+ NotProjectOwnerForVerification = 302,
+ /// Verification fee has not been paid for this project.
+ FeeNotPaid = 303,
+ /// Evidence CID is empty or invalid.
+ InvalidEvidenceCid = 304,
+ /// Only admin or authorized verifier can approve or reject.
+ UnauthorizedVerifier = 305,
+ /// Verification is not in Pending state (already approved or rejected).
+ VerificationNotPending = 306,
+
+ // ---- Fee manager (4xx) ----
+ /// Only admin can set fee configuration.
+ UnauthorizedAdmin = 401,
+ /// Fee amount must be greater than zero when fee is enabled.
+ InvalidFeeAmount = 402,
+ /// Treasury address is invalid.
+ InvalidTreasury = 403,
+ /// Payment failed (transfer to treasury failed).
+ PaymentFailed = 404,
+ /// Fee configuration not set (token/amount/treasury).
+ FeeNotConfigured = 405,
}
diff --git a/dongle-smartcontract/src/events.rs b/dongle-smartcontract/src/events.rs
index 4946d5a..6a85963 100644
--- a/dongle-smartcontract/src/events.rs
+++ b/dongle-smartcontract/src/events.rs
@@ -1,175 +1,92 @@
-//! Contract events for all state-changing actions. Emitted via env.events().publish.
+//! Contract events for all state-changing actions. Emitted consistently for indexing and clients.
-use soroban_sdk::{Address, Env, String, Symbol};
+use soroban_sdk::{contractevent, Address, String};
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProjectRegistered {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub owner: Address,
pub name: String,
pub category: String,
}
-impl ProjectRegistered {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "ProjectRegistered"),
- self.project_id,
- self.owner,
- ),
- (self.name, self.category),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProjectUpdated {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub owner: Address,
pub updated_at: u64,
}
-impl ProjectUpdated {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "ProjectUpdated"),
- self.project_id,
- self.owner,
- ),
- (self.updated_at,),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReviewAdded {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub reviewer: Address,
pub rating: u32,
}
-impl ReviewAdded {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "ReviewAdded"),
- self.project_id,
- self.reviewer,
- ),
- (self.rating,),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReviewUpdated {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub reviewer: Address,
pub rating: u32,
pub updated_at: u64,
}
-impl ReviewUpdated {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "ReviewUpdated"),
- self.project_id,
- self.reviewer,
- ),
- (self.rating, self.updated_at),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerificationRequested {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub requester: Address,
pub evidence_cid: String,
}
-impl VerificationRequested {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "VerificationRequested"),
- self.project_id,
- self.requester,
- ),
- (self.evidence_cid,),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerificationApproved {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub verifier: Address,
}
-impl VerificationApproved {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "VerificationApproved"),
- self.project_id,
- self.verifier,
- ),
- (),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerificationRejected {
+ #[topic]
pub project_id: u64,
+ #[topic]
pub verifier: Address,
}
-impl VerificationRejected {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (
- Symbol::new(env, "VerificationRejected"),
- self.project_id,
- self.verifier,
- ),
- (),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FeePaid {
+ #[topic]
pub payer: Address,
+ #[topic]
pub project_id: u64,
pub amount: u128,
}
-impl FeePaid {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (Symbol::new(env, "FeePaid"), self.payer, self.project_id),
- (self.amount,),
- );
- }
-}
-
+#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FeeSet {
+ #[topic]
pub admin: Address,
pub amount: u128,
pub treasury: Address,
}
-
-impl FeeSet {
- pub fn publish(self, env: &Env) {
- env.events().publish(
- (Symbol::new(env, "FeeSet"), self.admin),
- (self.amount, self.treasury),
- );
- }
-}
diff --git a/dongle-smartcontract/src/fee_manager.rs b/dongle-smartcontract/src/fee_manager.rs
index 02b0bf0..dd4ea30 100644
--- a/dongle-smartcontract/src/fee_manager.rs
+++ b/dongle-smartcontract/src/fee_manager.rs
@@ -1,6 +1,5 @@
//! Fee configuration and payment with validation and events.
-
-use crate::errors::Error;
+use crate::errors::ContractError;
use crate::events::FeePaid;
use crate::events::FeeSet;
use crate::storage_keys::StorageKey;
@@ -11,77 +10,137 @@ use soroban_sdk::{Address, Env};
pub struct FeeManager;
impl FeeManager {
- pub fn set_fee_config(
- _env: &Env,
- _admin: Address,
- _token: Option
,
- _verification_fee: u128,
- _registration_fee: u128,
- _treasury: Address,
- ) -> Result<(), ContractError> {
- todo!("Fee configuration logic not implemented")
- }
-
- pub fn pay_fee(
- _env: &Env,
- _payer: Address,
- _operation_type: &str,
- _project_id: Option,
- ) -> Result<(), ContractError> {
- todo!("Fee payment logic not implemented")
- }
-
- pub fn get_fee_config(_env: &Env) -> Result {
- todo!("Fee configuration retrieval logic not implemented")
+ pub fn set_admin(env: &Env, admin: Address) {
+ env.storage().persistent().set(&StorageKey::Admin, &admin);
}
- pub fn set_treasury(
- _env: &Env,
- _admin: Address,
- _treasury: Address,
+ /// Sets fee config with separate verification and registration fees.
+ /// Called from lib.rs set_fee_config.
+ pub fn set_fee_config(
+ env: &Env,
+ admin: &Address,
+ token: Option,
+ verification_fee: u128,
+ registration_fee: u128,
) -> Result<(), ContractError> {
- todo!("Treasury setting logic not implemented")
- }
+ admin.require_auth();
+ let current_admin: Option = env.storage().persistent().get(&StorageKey::Admin);
+ if current_admin.as_ref() != Some(admin) {
+ return Err(ContractError::UnauthorizedAdmin);
+ }
+ if verification_fee == 0 && registration_fee == 0 {
+ return Err(ContractError::InvalidFeeAmount);
+ }
+ let treasury: Address = env
+ .storage()
+ .persistent()
+ .get(&StorageKey::Treasury)
+ .ok_or(ContractError::InvalidTreasury)?;
- pub fn get_treasury(_env: &Env) -> Result {
- todo!("Treasury address retrieval logic not implemented")
- }
+ let config = FeeConfig {
+ token,
+ amount: verification_fee,
+ treasury: treasury.clone(),
+ };
+ env.storage()
+ .persistent()
+ .set(&StorageKey::FeeConfig, &config);
- pub fn get_operation_fee(_env: &Env, operation_type: &str) -> Result {
- match operation_type {
- "verification" => Ok(1000000),
- "registration" => Ok(0),
- _ => Err(ContractError::InvalidProjectData),
+ FeeSet {
+ admin: admin.clone(),
+ amount: verification_fee,
+ treasury,
}
+ .publish(env);
+ Ok(())
}
- pub fn fee_config_exists(_env: &Env) -> bool {
- false
+ /// Legacy single-fee setter (used internally).
+ pub fn set_fee(
+ env: &Env,
+ admin: Address,
+ token: Option,
+ amount: u128,
+ treasury: Address,
+ ) -> Result<(), ContractError> {
+ admin.require_auth();
+ let current_admin: Option = env.storage().persistent().get(&StorageKey::Admin);
+ if current_admin.as_ref() != Some(&admin) {
+ return Err(ContractError::UnauthorizedAdmin);
+ }
+ if amount == 0 {
+ return Err(ContractError::InvalidFeeAmount);
+ }
+ let config = FeeConfig {
+ token,
+ amount,
+ treasury: treasury.clone(),
+ };
+ env.storage()
+ .persistent()
+ .set(&StorageKey::FeeConfig, &config);
+ FeeSet {
+ admin,
+ amount,
+ treasury,
+ }
+ .publish(env);
+ Ok(())
}
- pub fn treasury_exists(_env: &Env) -> bool {
- false
+ fn get_config(env: &Env) -> Result {
+ env.storage()
+ .persistent()
+ .get(&StorageKey::FeeConfig)
+ .ok_or(ContractError::FeeNotConfigured)
}
- // Mark fee as paid for this project so verification can proceed.
+ pub fn pay_fee(
+ env: &Env,
+ payer: Address,
+ project_id: u64,
+ _token: Option,
+ ) -> Result<(), ContractError> {
+ let config = Self::get_config(env)?;
+ if config.amount == 0 {
+ return Err(ContractError::InvalidFeeAmount);
+ }
VerificationRegistry::set_fee_paid(env, project_id);
-
FeePaid {
payer: payer.clone(),
project_id,
amount: config.amount,
}
.publish(env);
-
Ok(())
}
- pub fn refund_fee(
- _env: &Env,
- _recipient: Address,
- _amount: u128,
- _token: Option,
+ pub fn get_fee_config(env: &Env) -> Result {
+ Self::get_config(env)
+ }
+
+ /// Sets the treasury address. Caller must be admin.
+ pub fn set_treasury(
+ env: &Env,
+ admin: &Address,
+ treasury: Address,
) -> Result<(), ContractError> {
- todo!("Fee refund logic not implemented")
+ admin.require_auth();
+ let current_admin: Option = env.storage().persistent().get(&StorageKey::Admin);
+ if current_admin.as_ref() != Some(admin) {
+ return Err(ContractError::UnauthorizedAdmin);
+ }
+ env.storage()
+ .persistent()
+ .set(&StorageKey::Treasury, &treasury);
+ Ok(())
+ }
+
+ /// Returns the current treasury address.
+ pub fn get_treasury(env: &Env) -> Result {
+ env.storage()
+ .persistent()
+ .get(&StorageKey::Treasury)
+ .ok_or(ContractError::InvalidTreasury)
}
}
diff --git a/dongle-smartcontract/src/lib.rs b/dongle-smartcontract/src/lib.rs
index 2b158cd..9c4fbbd 100644
--- a/dongle-smartcontract/src/lib.rs
+++ b/dongle-smartcontract/src/lib.rs
@@ -1,38 +1,49 @@
#![no_std]
-#![allow(dead_code)]
-#![allow(clippy::too_many_arguments)]
//! Dongle Smart Contract: project registry, reviews, and verification on Stellar/Soroban.
-mod constants;
-mod errors;
-mod events;
-mod fee_manager;
-mod project_registry;
-mod review_registry;
-mod storage_keys;
-mod types;
-mod utils;
-mod verification_registry;
+pub mod constants;
+pub mod errors;
+pub mod events;
+pub mod fee_manager;
+pub mod project_registry;
+pub mod rating_calculator;
+pub mod review_registry;
+pub mod storage_keys;
+pub mod types;
+pub mod utils;
+pub mod verification_registry;
use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec};
-use crate::errors::ContractError;
-use crate::project_registry::ProjectRegistry;
-use crate::types::{FeeConfig, Project, Review, VerificationRecord};
+use errors::ContractError;
+use fee_manager::FeeManager;
+use project_registry::ProjectRegistry;
+use review_registry::ReviewRegistry;
+use storage_keys::StorageKey;
+use types::{FeeConfig, Project, Review, VerificationRecord};
+use verification_registry::VerificationRegistry;
#[contract]
pub struct DongleContract;
#[contractimpl]
impl DongleContract {
- pub fn initialize(_env: Env, _admin: Address, _treasury: Address) -> Result<(), Error> {
- todo!("Contract initialization not yet implemented")
+ /// Initializes the contract with an admin and treasury address.
+ pub fn initialize(env: Env, admin: Address, treasury: Address) -> Result<(), ContractError> {
+ admin.require_auth();
+ env.storage().persistent().set(&StorageKey::Admin, &admin);
+ env.storage()
+ .persistent()
+ .set(&StorageKey::Treasury, &treasury);
+ Ok(())
}
- pub fn set_admin(_env: Env, _caller: Address, _new_admin: Address) -> Result<(), Error> {
- todo!("Admin management not yet implemented")
+ /// Sets the admin. Caller must be the existing admin (or anyone if unset).
+ pub fn set_admin(env: Env, caller: Address, new_admin: Address) -> Result<(), ContractError> {
+ utils::Utils::add_admin(&env, &caller, &new_admin)
}
+ /// Registers a new project and returns its ID.
pub fn register_project(
env: Env,
owner: Address,
@@ -42,8 +53,8 @@ impl DongleContract {
website: Option,
logo_cid: Option,
metadata_cid: Option,
- ) -> Result {
- project_registry::ProjectRegistry::register_project(
+ ) -> Result {
+ ProjectRegistry::register_project(
&env,
owner,
name,
@@ -55,6 +66,7 @@ impl DongleContract {
)
}
+ /// Updates an existing project. Caller must be the project owner.
pub fn update_project(
env: Env,
project_id: u64,
@@ -66,7 +78,6 @@ impl DongleContract {
logo_cid: Option,
metadata_cid: Option,
) -> Result<(), ContractError> {
- // ACTUAL IMPLEMENTATION: Replacing todo!() with our secure logic
ProjectRegistry::update_project(
&env,
project_id,
@@ -80,121 +91,171 @@ impl DongleContract {
)
}
+ /// Returns a project by ID, or error if not found.
pub fn get_project(env: Env, project_id: u64) -> Result {
- // ACTUAL IMPLEMENTATION: Replacing todo!() with our retrieval logic
- ProjectRegistry::get_project(&env, project_id).ok_or(ContractError::ProjectNotFound)
+ ProjectRegistry::get_project(&env, project_id)?.ok_or(ContractError::ProjectNotFound)
}
+ /// Returns the number of projects registered by an owner.
+ pub fn get_owner_project_count(env: Env, owner: Address) -> u32 {
+ ProjectRegistry::get_owner_project_count(&env, &owner)
+ }
+
+ /// Returns a paginated list of projects starting from start_id.
pub fn list_projects(
- _env: Env,
- _start_id: u64,
- _limit: u32,
+ env: Env,
+ start_id: u64,
+ limit: u32,
) -> Result, ContractError> {
- todo!("Project listing not yet implemented")
+ let mut results = Vec::new(&env);
+ let mut id = if start_id == 0 { 1 } else { start_id };
+ let mut count = 0u32;
+ let max = limit.min(100);
+ while count < max {
+ match ProjectRegistry::get_project(&env, id)? {
+ Some(p) => {
+ results.push_back(p);
+ count += 1;
+ }
+ None => break,
+ }
+ id += 1;
+ }
+ Ok(results)
}
+ /// Submits a review for a project. Only the CID is stored on-chain; text lives on IPFS.
pub fn add_review(
- _env: Env,
- _project_id: u64,
- _reviewer: Address,
- _rating: u32,
- _comment_cid: Option,
- ) -> Result<(), Error> {
- todo!("Review submission not yet implemented")
+ env: Env,
+ project_id: u64,
+ reviewer: Address,
+ rating: u32,
+ comment_cid: Option,
+ ) -> Result<(), ContractError> {
+ ReviewRegistry::add_review(&env, project_id, reviewer, rating, comment_cid)
}
+ /// Updates an existing review. Caller must be the original reviewer.
pub fn update_review(
- _env: Env,
- _project_id: u64,
- _reviewer: Address,
- _rating: u32,
- _comment_cid: Option,
- ) -> Result<(), Error> {
- todo!("Review updates not yet implemented")
+ env: Env,
+ project_id: u64,
+ reviewer: Address,
+ rating: u32,
+ comment_cid: Option,
+ ) -> Result<(), ContractError> {
+ ReviewRegistry::update_review(&env, project_id, reviewer, rating, comment_cid)
}
- pub fn get_review(_env: Env, _project_id: u64, _reviewer: Address) -> Result {
- todo!("Review retrieval not yet implemented")
+ /// Returns a specific review, or error if not found.
+ /// The `comment_cid` field is used by the frontend to fetch review text from IPFS.
+ pub fn get_review(
+ env: Env,
+ project_id: u64,
+ reviewer: Address,
+ ) -> Result {
+ ReviewRegistry::get_review(&env, project_id, reviewer)
+ .ok_or(ContractError::ReviewNotFound)
}
+ /// Returns reviews for a project (stub — full pagination requires an index).
pub fn get_project_reviews(
- _env: Env,
+ env: Env,
_project_id: u64,
_start_reviewer: Option,
_limit: u32,
- ) -> Result, Error> {
- todo!("Project review listing not yet implemented")
+ ) -> Result, ContractError> {
+ Ok(Vec::new(&env))
}
+ /// Requests verification for a project.
pub fn request_verification(
- _env: Env,
- _project_id: u64,
- _requester: Address,
- _evidence_cid: String,
+ env: Env,
+ project_id: u64,
+ requester: Address,
+ evidence_cid: String,
) -> Result<(), ContractError> {
- todo!("Verification requests not yet implemented")
+ VerificationRegistry::request_verification(&env, project_id, requester, evidence_cid)
}
+ /// Approves a pending verification. Caller must be admin.
pub fn approve_verification(
- _env: Env,
- _project_id: u64,
- _admin: Address,
+ env: Env,
+ project_id: u64,
+ admin: Address,
) -> Result<(), ContractError> {
- todo!("Verification approval not yet implemented")
+ VerificationRegistry::approve_verification(&env, project_id, admin)
}
+ /// Rejects a pending verification. Caller must be admin.
pub fn reject_verification(
- _env: Env,
- _project_id: u64,
- _admin: Address,
+ env: Env,
+ project_id: u64,
+ admin: Address,
) -> Result<(), ContractError> {
- todo!("Verification rejection not yet implemented")
+ VerificationRegistry::reject_verification(&env, project_id, admin)
}
+ /// Returns the verification record for a project, or error if not found.
pub fn get_verification(
- _env: Env,
- _project_id: u64,
+ env: Env,
+ project_id: u64,
) -> Result {
- todo!("Verification record retrieval not yet implemented")
+ VerificationRegistry::get_verification(&env, project_id)
+ .ok_or(ContractError::VerificationNotFound)
}
+ /// Sets the fee configuration. Caller must be admin.
pub fn set_fee_config(
- _env: Env,
- _admin: Address,
- _token: Option,
- _verification_fee: u128,
- _registration_fee: u128,
+ env: Env,
+ admin: Address,
+ token: Option,
+ verification_fee: u128,
+ registration_fee: u128,
) -> Result<(), ContractError> {
- todo!("Fee configuration not yet implemented")
+ FeeManager::set_fee_config(&env, &admin, token, verification_fee, registration_fee)
}
- pub fn get_fee_config(_env: Env) -> Result {
- todo!("Fee configuration retrieval not yet implemented")
+ /// Convenience fee setter with an explicit treasury address (used in tests / simple flows).
+ /// Stores the treasury, then sets the fee config.
+ pub fn set_fee(
+ env: Env,
+ admin: Address,
+ token: Option,
+ amount: u128,
+ treasury: Address,
+ ) -> Result<(), ContractError> {
+ // Persist the treasury so pay_fee can reference it.
+ env.storage()
+ .persistent()
+ .set(&StorageKey::Treasury, &treasury);
+ FeeManager::set_fee(&env, admin, token, amount, treasury)
}
- pub fn set_treasury(
- _env: Env,
- _admin: Address,
- _treasury: Address,
+ /// Pays the verification fee for a project, marking it eligible for verification.
+ pub fn pay_fee(
+ env: Env,
+ payer: Address,
+ project_id: u64,
+ token: Option,
) -> Result<(), ContractError> {
- todo!("Treasury management not yet implemented")
+ FeeManager::pay_fee(&env, payer, project_id, token)
}
- pub fn get_treasury(_env: Env) -> Result {
- todo!("Treasury address retrieval not yet implemented")
+ /// Returns the current fee configuration.
+ pub fn get_fee_config(env: Env) -> Result {
+ FeeManager::get_fee_config(&env)
}
-}
-#[cfg(test)]
-mod test {
- use super::*;
- use soroban_sdk::testutils::Address as _;
+ /// Sets the treasury address. Caller must be admin.
+ pub fn set_treasury(env: Env, admin: Address, treasury: Address) -> Result<(), ContractError> {
+ FeeManager::set_treasury(&env, &admin, treasury)
+ }
- #[test]
- fn test_unauthorized_update_fails() {
- let env = Env::default();
- let _owner = Address::generate(&env);
- let _hacker = Address::generate(&env);
- // Note: Full test logic will require a project to be registered first
+ /// Returns the current treasury address.
+ pub fn get_treasury(env: Env) -> Result {
+ FeeManager::get_treasury(&env)
}
}
+
+#[cfg(test)]
+mod test;
diff --git a/dongle-smartcontract/src/project_registry.rs b/dongle-smartcontract/src/project_registry.rs
index fe3910d..4601cf9 100644
--- a/dongle-smartcontract/src/project_registry.rs
+++ b/dongle-smartcontract/src/project_registry.rs
@@ -1,14 +1,61 @@
+//! Project registration with validation, per-user limits, and events.
+use crate::constants::*;
use crate::errors::ContractError;
-use crate::types::{DataKey, Project};
-use soroban_sdk::{Address, Env, String, Vec};
+use crate::events::ProjectRegistered;
+use crate::events::ProjectUpdated;
+use crate::storage_keys::StorageKey;
+use crate::types::Project;
+use soroban_sdk::{Address, Env, String};
+
+fn validate_string_length(s: &String, max: usize) -> Result<(), ContractError> {
+ if s.len() as usize > max {
+ return Err(ContractError::StringLengthExceeded);
+ }
+ Ok(())
+}
+
+fn validate_optional_string(s: &Option, max: usize) -> Result<(), ContractError> {
+ if let Some(ref x) = s {
+ validate_string_length(x, max)?;
+ }
+ Ok(())
+}
+
+pub fn validate_project_inputs(
+ name: &String,
+ description: &String,
+ category: &String,
+ website: &Option,
+ logo_cid: &Option,
+ metadata_cid: &Option,
+) -> Result<(), ContractError> {
+ // Soroban String has no trim() — check raw len against MIN_STRING_LEN
+ if (name.len() as usize) < MIN_STRING_LEN {
+ return Err(ContractError::InvalidProjectName);
+ }
+ if (description.len() as usize) < MIN_STRING_LEN {
+ return Err(ContractError::InvalidProjectDescription);
+ }
+ if (category.len() as usize) < MIN_STRING_LEN {
+ return Err(ContractError::InvalidProjectCategory);
+ }
+ validate_string_length(name, MAX_NAME_LEN)?;
+ validate_string_length(description, MAX_DESCRIPTION_LEN)?;
+ validate_string_length(category, MAX_CATEGORY_LEN)?;
+ validate_optional_string(website, MAX_WEBSITE_LEN)?;
+ validate_optional_string(logo_cid, MAX_CID_LEN)?;
+ validate_optional_string(metadata_cid, MAX_CID_LEN)?;
+ Ok(())
+}
pub struct ProjectRegistry;
impl ProjectRegistry {
fn next_project_id(env: &Env) -> u64 {
- let key = StorageKey::NextProjectId;
- let next: u64 = env.storage().persistent().get(&key).unwrap_or(1);
- next
+ env.storage()
+ .persistent()
+ .get(&StorageKey::NextProjectId)
+ .unwrap_or(1)
}
fn set_next_project_id(env: &Env, id: u64) {
@@ -33,22 +80,64 @@ impl ProjectRegistry {
pub fn register_project(
env: &Env,
- _owner: Address,
- _name: String,
- _description: String,
- _category: String,
- _website: Option,
- _logo_cid: Option,
- _metadata_cid: Option,
+ owner: Address,
+ name: String,
+ description: String,
+ category: String,
+ website: Option,
+ logo_cid: Option,
+ metadata_cid: Option,
) -> Result {
- let _registered_at: u64 = env.ledger().timestamp();
- todo!("Project registration logic not implemented")
- }
+ owner.require_auth();
+
+ validate_project_inputs(
+ &name,
+ &description,
+ &category,
+ &website,
+ &logo_cid,
+ &metadata_cid,
+ )?;
+
+ let count = Self::owner_project_count(env, &owner);
+ if count >= MAX_PROJECTS_PER_USER {
+ return Err(ContractError::MaxProjectsPerUserExceeded);
+ }
+
+ let project_id = Self::next_project_id(env);
+ if project_id == 0 {
+ return Err(ContractError::InvalidProjectId);
+ }
+
+ let ledger_timestamp = env.ledger().timestamp();
+ let project = Project {
+ id: project_id,
+ owner: owner.clone(),
+ name: name.clone(),
+ description: description.clone(),
+ category: category.clone(),
+ website: website.clone(),
+ logo_cid: logo_cid.clone(),
+ metadata_cid: metadata_cid.clone(),
+ created_at: ledger_timestamp,
+ updated_at: ledger_timestamp,
+ };
- pub fn get_project(env: &Env, project_id: u64) -> Option {
env.storage()
.persistent()
- .get(&DataKey::Project(project_id))
+ .set(&StorageKey::Project(project_id), &project);
+ Self::set_next_project_id(env, project_id);
+ Self::inc_owner_project_count(env, &owner);
+
+ ProjectRegistered {
+ project_id,
+ owner: owner.clone(),
+ name: name.clone(),
+ category: category.clone(),
+ }
+ .publish(env);
+
+ Ok(project_id)
}
pub fn update_project(
@@ -62,86 +151,65 @@ impl ProjectRegistry {
logo_cid: Option,
metadata_cid: Option,
) -> Result<(), ContractError> {
- // 1. AUTHENTICATION: Verify the user's cryptographic signature
caller.require_auth();
- // 2. RETRIEVAL: Check if project exists
- let mut project: Project =
- Self::get_project(env, project_id).ok_or(ContractError::ProjectNotFound)?;
+ if project_id == 0 {
+ return Err(ContractError::InvalidProjectId);
+ }
+
+ let mut project: Project = env
+ .storage()
+ .persistent()
+ .get(&StorageKey::Project(project_id))
+ .ok_or(ContractError::ProjectNotFound)?;
- // 3. AUTHORIZATION: Verify the caller is the stored owner
- if caller != project.owner {
- return Err(ContractError::Unauthorized);
+ if project.owner != caller {
+ return Err(ContractError::NotProjectOwner);
}
- // 4. DATA VALIDATION
- Self::validate_project_data(&name, &description, &category)?;
+ validate_project_inputs(
+ &name,
+ &description,
+ &category,
+ &website,
+ &logo_cid,
+ &metadata_cid,
+ )?;
- // 5. UPDATE FIELDS
+ let ledger_timestamp = env.ledger().timestamp();
project.name = name;
project.description = description;
project.category = category;
project.website = website;
project.logo_cid = logo_cid;
project.metadata_cid = metadata_cid;
+ project.updated_at = ledger_timestamp;
- // Update the timestamp to current ledger time
- project.updated_at = env.ledger().timestamp();
-
- // 6. PERSISTENCE: Save back to storage
env.storage()
.persistent()
- .set(&DataKey::Project(project_id), &project);
-
- Ok(())
- }
-
- /// Returns the number of projects registered by an owner (for tests and admin).
- pub fn get_owner_project_count(env: &Env, owner: &Address) -> u32 {
- Self::owner_project_count(env, owner)
- }
-}
+ .set(&StorageKey::Project(project_id), &project);
-// ── Tests ─────────────────────────────────────────────────────────────────────
-
-#[cfg(test)]
-mod tests {
- use crate::{DongleContract, DongleContractClient};
- use soroban_sdk::{
- testutils::{Address as _, Events, Ledger, LedgerInfo},
- Address, Env, String,
- };
-
- fn ledger_at(timestamp: u64) -> LedgerInfo {
- LedgerInfo {
- timestamp,
- protocol_version: 20,
- sequence_number: 1,
- network_id: Default::default(),
- base_reserve: 10,
- min_temp_entry_ttl: 16,
- min_persistent_entry_ttl: 100_000,
- max_entry_ttl: 10_000_000,
+ ProjectUpdated {
+ project_id,
+ owner: caller,
+ updated_at: ledger_timestamp,
}
- }
+ .publish(env);
- fn setup(env: &Env) -> DongleContractClient {
- let contract_id = env.register_contract(None, DongleContract);
- DongleContractClient::new(env, &contract_id)
+ Ok(())
}
- pub fn project_exists(env: &Env, project_id: u64) -> bool {
- env.storage()
+ pub fn get_project(env: &Env, project_id: u64) -> Result