diff --git a/Cargo.lock b/Cargo.lock index 29eb5905..5e0c5a5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,21 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "2.11.0" @@ -194,7 +209,6 @@ dependencies = [ name = "commitment_core" version = "0.1.0" dependencies = [ - "commitment_nft", "shared_utils", "soroban-sdk", ] @@ -212,6 +226,9 @@ name = "commitment_nft" version = "0.1.0" dependencies = [ "commitment_core", + "ed25519-dalek", + "rand 0.10.0", + "serde_json", "shared_utils", "soroban-sdk", ] @@ -279,9 +296,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -516,6 +533,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "escape-bytes" version = "0.1.1" @@ -528,6 +555,12 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "ff" version = "0.13.1" @@ -564,9 +597,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -586,6 +619,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.2" @@ -594,7 +639,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.0", "wasip2", "wasip3", @@ -750,9 +795,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.92" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "once_cell", "wasm-bindgen", @@ -787,9 +832,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libm" @@ -797,6 +842,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -964,6 +1015,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.45" @@ -973,6 +1049,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -986,10 +1068,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + [[package]] name = "rand" version = "0.10.0" @@ -1011,6 +1103,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -1020,12 +1122,30 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_core" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -1046,6 +1166,12 @@ dependencies = [ "syn", ] +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + [[package]] name = "rfc6979" version = "0.4.0" @@ -1071,12 +1197,37 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "schemars" version = "0.9.0" @@ -1219,6 +1370,7 @@ dependencies = [ name = "shared_utils" version = "0.1.0" dependencies = [ + "proptest", "soroban-sdk", ] @@ -1306,7 +1458,7 @@ dependencies = [ "num-traits", "p256", "rand 0.8.5", - "rand_chacha", + "rand_chacha 0.3.1", "sec1", "sha2", "sha3", @@ -1502,6 +1654,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1567,6 +1732,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1586,19 +1757,21 @@ dependencies = [ "soroban-sdk", ] -[[package]] -name = "version-system" -version = "0.0.0" -dependencies = [ - "soroban-sdk", -] - [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1625,9 +1798,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.115" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -1638,9 +1811,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.115" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1648,9 +1821,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.115" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -1661,9 +1834,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.115" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -1798,6 +1971,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/contracts/commitment_core/src/benchmark_invariant_tests.rs b/contracts/commitment_core/src/benchmark_invariant_tests.rs index d913d7e1..68e6fcc3 100644 --- a/contracts/commitment_core/src/benchmark_invariant_tests.rs +++ b/contracts/commitment_core/src/benchmark_invariant_tests.rs @@ -12,7 +12,7 @@ //! all active commitments after every create / settle / early-exit. //! 3. **Commitment ID uniqueness** — `generate_commitment_id` produces a distinct string //! for every counter value in `[0, N)`. -//! 4. **ID format** — every generated ID starts with the prefix `"c_"`. +//! 4. **ID format** — every generated ID starts with the prefix `"COMMIT_"`. //! 5. **Violation predicate correctness** — `check_violations` returns `true` iff //! `loss_percent > max_loss_percent` OR `now >= expires_at`; returns `false` for //! non-active commitments (no false positives on settled/exited state). @@ -29,7 +29,7 @@ #![cfg(test)] use super::*; -use soroban_sdk::{testutils::Address as _, Address, Env, String}; +use soroban_sdk::{testutils::{Address as _, Ledger}, Address, Env, String}; // --------------------------------------------------------------------------- // Shared helpers @@ -179,7 +179,7 @@ fn invariant_tvl_equals_sum_of_seeded_amounts() { }); let amounts = [1_000i128, 2_000, 3_000, 4_000, 5_000]; - let ids = ["c_0", "c_1", "c_2", "c_3", "c_4"]; + let ids = ["COMMIT_0", "COMMIT_1", "COMMIT_2", "COMMIT_3", "COMMIT_4"]; let expected_tvl: i128 = amounts.iter().sum(); for (&id, &amt) in ids.iter().zip(amounts.iter()) { @@ -227,46 +227,38 @@ fn invariant_commitment_ids_are_unique() { assert_eq!(ids.len(), n as u32); } -/// Invariant: every generated ID starts with the prefix "c_". +/// Invariant: every generated ID starts with the prefix "COMMIT_". #[test] fn invariant_commitment_id_prefix() { let e = Env::default(); for i in [0u64, 1, 9, 10, 99, 100, 999, 1_000, u32::MAX as u64] { let id = CommitmentCoreContract::generate_commitment_id(&e, i); - // The first two bytes of the underlying string must be 'c' and '_' + // The first seven bytes must be "COMMIT_" assert!( - id.len() >= 2, + id.len() >= 7, "ID too short for counter {}", i ); - // Verify prefix by comparing against known prefix string - let c_prefix = String::from_str(&e, "c_"); - // Compare first two chars: build "c_X" and check id starts with "c_" - // We verify by constructing the expected prefix and checking id != a non-prefixed string - let bad_prefix = String::from_str(&e, "x_"); - assert!(id != bad_prefix, "ID must not start with 'x_'"); - // Positive check: id must equal String::from_str(&e, &format!("c_{}", i)) - // We can't use format! in no_std, so we verify via generate_commitment_id round-trip: - // counter 0 → "c_0", counter 1 → "c_1" (verified in dedicated tests above) - // Here we just assert the id contains the prefix by checking it differs from a non-prefixed variant - let _ = c_prefix; + // Verify prefix does not start with a wrong prefix + let bad_prefix = String::from_str(&e, "c_"); + assert!(id != bad_prefix, "ID must not equal 'c_'"); } } -/// Invariant: counter 0 produces "c_0". +/// Invariant: counter 0 produces "COMMIT_0". #[test] fn invariant_commitment_id_counter_zero() { let e = Env::default(); let id = CommitmentCoreContract::generate_commitment_id(&e, 0); - assert_eq!(id, String::from_str(&e, "c_0")); + assert_eq!(id, String::from_str(&e, "COMMIT_0")); } -/// Invariant: counter 1 produces "c_1". +/// Invariant: counter 1 produces "COMMIT_1". #[test] fn invariant_commitment_id_counter_one() { let e = Env::default(); let id = CommitmentCoreContract::generate_commitment_id(&e, 1); - assert_eq!(id, String::from_str(&e, "c_1")); + assert_eq!(id, String::from_str(&e, "COMMIT_1")); } /// Invariant: large counter value encodes correctly. @@ -274,7 +266,7 @@ fn invariant_commitment_id_counter_one() { fn invariant_commitment_id_large_counter() { let e = Env::default(); let id = CommitmentCoreContract::generate_commitment_id(&e, 123_456_789); - assert_eq!(id, String::from_str(&e, "c_123456789")); + assert_eq!(id, String::from_str(&e, "COMMIT_123456789")); } // --------------------------------------------------------------------------- @@ -474,7 +466,7 @@ fn invariant_settle_post_conditions() { assert_eq!(tvl, 0, "TVL must be 0 after settling the only commitment"); let owner_list = e.as_contract(&contract_id, || { - CommitmentCoreContract::get_owner_commitments(e.clone(), owner.clone()) + CommitmentCoreContract::list_commitments_by_owner(e.clone(), owner.clone()) }); assert_eq!( owner_list.len(), diff --git a/contracts/commitment_core/src/emergency_tests.rs b/contracts/commitment_core/src/emergency_tests.rs index f5e54cf2..e1bf9552 100644 --- a/contracts/commitment_core/src/emergency_tests.rs +++ b/contracts/commitment_core/src/emergency_tests.rs @@ -3,7 +3,7 @@ use super::*; use soroban_sdk::{ contract, contractimpl, symbol_short, - testutils::{Address as _, Events}, + testutils::{Address as _, Events, Ledger}, token::{Client as TokenClient, StellarAssetClient}, Address, Env, IntoVal, String, }; @@ -83,28 +83,32 @@ fn test_emergency_mode_toggle_emits_events() { assert!(!client.is_emergency_mode()); let events = e.events().all(); - let emg_mode_symbol = symbol_short!("EmgMode").into_val(&e); - let emg_on = symbol_short!("EMG_ON").into_val(&e); - let emg_off = symbol_short!("EMG_OFF").into_val(&e); - - let mode_events: std::vec::Vec<_> = events - .iter() - .filter(|event| { - event.0 == contract_id - && event - .1 - .first() - .map_or(false, |topic| topic.shallow_eq(&emg_mode_symbol)) - }) - .collect(); - - assert_eq!(mode_events.len(), 2); - assert!(mode_events[0] - .2 - .shallow_eq(&(emg_on, e.ledger().timestamp()).into_val(&e))); - assert!(mode_events[1] - .2 - .shallow_eq(&(emg_off, e.ledger().timestamp()).into_val(&e))); + let emg_mode_symbol: soroban_sdk::Val = symbol_short!("EmgMode").into_val(&e); + let emg_on: soroban_sdk::Val = symbol_short!("EMG_ON").into_val(&e); + let emg_off: soroban_sdk::Val = symbol_short!("EMG_OFF").into_val(&e); + + let mut mode_event_count = 0u32; + let mut found_on = false; + let mut found_off = false; + for event in events.iter() { + let is_emg = event.0 == contract_id + && event + .1 + .first() + .map_or(false, |topic| topic.shallow_eq(&emg_mode_symbol)); + if is_emg { + mode_event_count += 1; + if event.2.shallow_eq(&(emg_on.clone(), e.ledger().timestamp()).into_val(&e)) { + found_on = true; + } + if event.2.shallow_eq(&(emg_off.clone(), e.ledger().timestamp()).into_val(&e)) { + found_off = true; + } + } + } + assert_eq!(mode_event_count, 2); + assert!(found_on, "EMG_ON event not found"); + assert!(found_off, "EMG_OFF event not found"); } #[test] @@ -134,7 +138,7 @@ fn test_create_commitment_forbidden_in_emergency_preserves_state() { assert!(client.is_emergency_mode()); assert_eq!(client.get_total_commitments(), 0); assert_eq!(client.get_total_value_locked(), 0); - assert_eq!(client.get_owner_commitments(&owner).len(), 0); + assert_eq!(client.list_commitments_by_owner(&owner).len(), 0); assert_eq!( client.get_commitments_created_between(&0, &u64::MAX).len(), 0 diff --git a/contracts/commitment_core/src/fee_tests.rs b/contracts/commitment_core/src/fee_tests.rs index 56e16665..0e9869e0 100644 --- a/contracts/commitment_core/src/fee_tests.rs +++ b/contracts/commitment_core/src/fee_tests.rs @@ -13,7 +13,8 @@ use crate::{CommitmentCoreContract, CommitmentCoreContractClient, CommitmentRule use soroban_sdk::{ contract, contractimpl, testutils::{Address as _, Ledger}, - token, Address, Env, String, + token::{self, Client as TokenClient, StellarAssetClient}, + Address, Env, String, }; #[contract] @@ -337,7 +338,7 @@ fn test_early_exit_with_creation_fee_and_penalty() { let exit_penalty = 99_000i128; // 10% of 990,000 let returned_to_user = net_amount - exit_penalty; let total_fees = creation_fee + exit_penalty; - let expected_user_balance = 10_000_000i128 - amount + expected_returned; + let expected_user_balance = 10_000_000i128 - amount + returned_to_user; // Verify both fees were collected assert_eq!(client.get_collected_fees(&token_address), total_fees); diff --git a/contracts/commitment_core/src/lib.rs b/contracts/commitment_core/src/lib.rs index a44af341..27c081e2 100644 --- a/contracts/commitment_core/src/lib.rs +++ b/contracts/commitment_core/src/lib.rs @@ -59,6 +59,8 @@ pub enum CommitmentError { InsufficientFees = 23, /// Arithmetic operation overflowed or underflowed ArithmeticOverflow = 24, + /// Generated commitment ID already exists (counter/storage corruption guard) + DuplicateCommitmentId = 25, } impl CommitmentError { @@ -90,6 +92,9 @@ impl CommitmentError { CommitmentError::FeeRecipientNotSet => "Fee recipient not set; cannot withdraw", CommitmentError::InsufficientFees => "Insufficient collected fees to withdraw", CommitmentError::ArithmeticOverflow => "Arithmetic overflow or underflow", + CommitmentError::DuplicateCommitmentId => { + "Commitment ID already exists; counter or storage may be corrupted" + } } } } @@ -165,6 +170,8 @@ pub enum DataKey { TotalValueLocked, AuthorizedAllocator(Address), AuthorizedUpdater(Address), + /// Ordered list of all authorized value updaters (for enumeration). + AuthorizedUpdaters, AuthorizedGuardian(Address), AuthorizedTreasurer(Address), AuthorizedOperator(Address), @@ -336,6 +343,9 @@ fn remove_authorized_updater(e: &Env, updater: &Address) { } } +/// Maximum number of items returned per paginated query. +const MAX_PAGE_SIZE: u32 = 50; + fn remove_from_owner_commitments(e: &Env, owner: &Address, commitment_id: &String) { let mut commitments: Vec = e .storage() @@ -392,18 +402,36 @@ impl CommitmentCoreContract { } } + /// Generate a canonical commitment ID in the format `COMMIT_`. + /// + /// The counter is the current value of `TotalCommitments` before incrementing, + /// making each ID deterministic and unique within this contract instance. + /// + /// # Format + /// `COMMIT_0`, `COMMIT_1`, ..., `COMMIT_18446744073709551615` + /// + /// # Security notes + /// - IDs are contract-generated; callers cannot influence the value. + /// - Uniqueness is guaranteed by the monotonically increasing `TotalCommitments` + /// counter, which is only incremented after successful commitment creation. fn generate_commitment_id(e: &Env, counter: u64) -> String { - let mut buf = [0u8; 32]; - buf[0] = b'c'; - buf[1] = b'_'; + // Prefix is 7 bytes: "COMMIT_" + let mut buf = [0u8; 28]; // 7 prefix + up to 20 decimal digits + NUL guard + buf[0] = b'C'; + buf[1] = b'O'; + buf[2] = b'M'; + buf[3] = b'M'; + buf[4] = b'I'; + buf[5] = b'T'; + buf[6] = b'_'; let mut n = counter; - let mut i = 2; + let mut i = 7usize; if n == 0 { buf[i] = b'0'; i += 1; } else { let mut digits = [0u8; 20]; - let mut count = 0; + let mut count = 0usize; while n > 0 { digits[count] = (n % 10) as u8 + b'0'; n /= 10; @@ -414,7 +442,16 @@ impl CommitmentCoreContract { i += 1; } } - String::from_str(e, core::str::from_utf8(&buf[..i]).unwrap_or("c_0")) + String::from_str(e, core::str::from_utf8(&buf[..i]).unwrap_or("COMMIT_0")) + } + + /// Return `true` if a commitment with the given ID already exists in storage. + /// + /// This is a read-only view; it performs no auth check. + pub fn commitment_id_exists(e: Env, commitment_id: String) -> bool { + e.storage() + .instance() + .has(&DataKey::Commitment(commitment_id)) } /// Initialize the core contract with its admin and linked NFT contract. The provided `nft_contract` becomes the downstream dependency used by @@ -496,19 +533,6 @@ impl CommitmentCoreContract { fail(&e, CommitmentError::ExpirationOverflow, "create") }); - // Calculate creation fee and net amount - let creation_fee_bps: u32 = e - .storage() - .instance() - .get(&DataKey::CreationFeeBps) - .unwrap_or(0); - let creation_fee = if creation_fee_bps > 0 { - fees::fee_from_bps(amount, creation_fee_bps) - } else { - 0 - }; - let net_amount = amount - creation_fee; - check_sufficient_balance(&e, &owner, &asset_address, amount); let current_total = e @@ -525,50 +549,18 @@ impl CommitmentCoreContract { fail(&e, CommitmentError::NotInitialized, "create") }); - // Calculate creation fee if configured - let creation_fee_bps: u32 = e - .storage() - .instance() - .get(&DataKey::CreationFeeBps) - .unwrap_or(0); - let creation_fee = if creation_fee_bps > 0 { - fees::fee_from_bps(amount, creation_fee_bps) - } else { - 0 - }; - // Net amount locked in commitment (after fee deduction) - let net_amount = amount - creation_fee; - - // Compute creation fee and net amount before any state writes so that - // `net_amount` is defined for both the Commitment struct and the TVL update. - let creation_fee_bps: u32 = e - .storage() - .instance() - .get(&DataKey::CreationFeeBps) - .unwrap_or(0); - let creation_fee = if creation_fee_bps > 0 { - fees::fee_from_bps(amount, creation_fee_bps) - } else { - 0 - }; - let net_amount = amount - creation_fee; - let commitment_id = Self::generate_commitment_id(&e, current_total); - // Calculate creation fee first - let creation_fee_bps: u32 = e - .storage() + // Uniqueness invariant: the counter-based ID must not already exist. + // Under normal operation this cannot happen; the assertion guards against + // future storage corruption or unexpected counter resets. + if e.storage() .instance() - .get(&DataKey::CreationFeeBps) - .unwrap_or(0); - let creation_fee = if creation_fee_bps > 0 { - fees::fee_from_bps(amount, creation_fee_bps) - } else { - 0 - }; - - // Net amount locked in commitment (after fee deduction) - let net_amount = amount - creation_fee; + .has(&DataKey::Commitment(commitment_id.clone())) + { + set_reentrancy_guard(&e, false); + fail(&e, CommitmentError::DuplicateCommitmentId, "create"); + } let commitment = Commitment { commitment_id: commitment_id.clone(), @@ -797,7 +789,7 @@ impl CommitmentCoreContract { pub fn add_allocator(e: Env, caller: Address, allocator: Address) { require_admin(&e, &caller); e.storage().instance().set( - &DataKey::AuthorizedAllocator(contract_address.clone()), + &DataKey::AuthorizedAllocator(allocator.clone()), &true, ); e.events().publish( diff --git a/contracts/commitment_core/src/tests.rs b/contracts/commitment_core/src/tests.rs index 0efddbde..3cd99158 100644 --- a/contracts/commitment_core/src/tests.rs +++ b/contracts/commitment_core/src/tests.rs @@ -676,7 +676,7 @@ fn test_create_commitment_expiration_overflow() { }; e.as_contract(&contract_id, || { - CommitmentCoreContract::create_commitment(e.clone(), owner, 1000, token_address, rules); + CommitmentCoreContract::create_commitment(e.clone(), owner, 1000, asset_address, rules); }); } @@ -1058,7 +1058,7 @@ fn test_get_owner_commitments_not_initialized_returns_empty() { let owner = Address::generate(&e); let commitments = e.as_contract(&contract_id, || { - CommitmentCoreContract::get_owner_commitments(e.clone(), owner.clone()) + CommitmentCoreContract::list_commitments_by_owner(e.clone(), owner.clone()) }); assert_eq!(commitments.len(), 0); } @@ -1178,7 +1178,7 @@ fn test_create_commitment_updates_storage_layout() { let created_id = client.create_commitment(&owner, &amount, &asset_address, &rules); let commitment = client.get_commitment(&created_id); - let owner_commitments = client.get_owner_commitments(&owner); + let owner_commitments = client.list_commitments_by_owner(&owner); let total_commitments = e.as_contract(&contract_id, || { e.storage() @@ -1250,7 +1250,7 @@ fn test_create_commitment_validation_failures_do_not_mutate_storage() { assert_eq!(client.get_total_commitments(), 0); assert_eq!(client.get_total_value_locked(), 0); - assert_eq!(client.get_owner_commitments(&owner).len(), 0); + assert_eq!(client.list_commitments_by_owner(&owner).len(), 0); assert_eq!( client.get_commitments_created_between(&0, &u64::MAX).len(), 0 @@ -1626,34 +1626,10 @@ fn test_check_violations_zero_amount() { #[test] fn test_create_commitment_event() { let e = Env::default(); - let contract_id = e.register_contract(None, CommitmentCoreContract); - let client = CommitmentCoreContractClient::new(&e, &contract_id); - let _owner = Address::generate(&e); - let admin = Address::generate(&e); - let nft_contract = Address::generate(&e); - - client.initialize(&admin, &nft_contract); - - let _rules = CommitmentRules { - duration_days: 30, - max_loss_percent: 10, - commitment_type: String::from_str(&e, "safe"), - early_exit_penalty: 15, - min_fee_threshold: 100, - grace_period_days: 0, - }; - - // Note: This might panic if mock token transfers are not set up, but we are testing events. - // However, create_commitment calls transfer_assets. - // We need to mock the token contract or use a test token. - // For simplicity, we might skip this test if it's too complex to mock everything here, - // OR we assume the user has set up mocks (which they haven't in this file). - // But wait, create_commitment calls `transfer_assets` which calls `token::Client::transfer`. - // If we don't have a real token contract, this will fail. - // `origin/master` tests use `create_test_commitment` helper which bypasses `create_commitment` logic. - // So `origin/master` tests don't test `create_commitment` fully? - // `test_create_commitment_valid` calls `validate_rules` directly. - // It seems `origin/master` avoids calling `create_commitment` because of dependencies. + e.mock_all_auths_allowing_non_root_auth(); + let amount = 1_000i128; + let (contract_id, client, owner, asset_address, _nft_contract, _token_client, rules) = + setup_create_commitment_fixture(&e, amount); let created_id = client.create_commitment(&owner, &amount, &asset_address, &rules); let created_symbol = symbol_short!("Created").into_val(&e); @@ -1670,8 +1646,8 @@ fn test_create_commitment_event() { .expect("created event should be emitted"); assert_eq!(created_event.1.len(), 3); - assert!(created_event.1[1].shallow_eq(&created_id.clone().into_val(&e))); - assert!(created_event.1[2].shallow_eq(&owner.clone().into_val(&e))); + assert!(created_event.1.get(1).unwrap().shallow_eq(&created_id.clone().into_val(&e))); + assert!(created_event.1.get(2).unwrap().shallow_eq(&owner.clone().into_val(&e))); assert!(created_event .2 .shallow_eq(&(amount, rules.clone(), 1u32, e.ledger().timestamp()).into_val(&e))); @@ -2025,7 +2001,7 @@ fn test_settle_success_expired() { assert_eq!(tvl, 0); let owner_commitments = e.as_contract(&contract_id, || { - CommitmentCoreContract::get_owner_commitments(e.clone(), owner.clone()) + CommitmentCoreContract::list_commitments_by_owner(e.clone(), owner.clone()) }); assert_eq!(owner_commitments.len(), 0); } @@ -2420,7 +2396,7 @@ fn test_tvl_consistency_sequence() { assert_eq!(client.get_total_value_locked(), 1000); // 2. Update Value -> TVL adjusts - client.update_value(&id, &1200); + client.update_value(&admin, &id, &1200); assert_eq!(client.get_total_value_locked(), 1200); // 3. Allocate -> TVL decreases @@ -3108,7 +3084,7 @@ fn test_owner_multiple_commitments_creation() { let client = CommitmentCoreContractClient::new(&e, &contract_id); // Verify owner has 3 commitments - let owner_commitments = client.get_owner_commitments(&owner); + let owner_commitments = client.list_commitments_by_owner(&owner); assert_eq!(owner_commitments.len(), 3); } @@ -3203,7 +3179,7 @@ fn test_owner_multiple_commitments_settle_one() { let client = CommitmentCoreContractClient::new(&e, &contract_id); // Verify owner still has 3 commitments in list - let owner_commitments = client.get_owner_commitments(&owner); + let owner_commitments = client.list_commitments_by_owner(&owner); assert_eq!(owner_commitments.len(), 3); // Verify settled commitment status changed @@ -3267,3 +3243,138 @@ fn test_role_management() { assert!(client.is_allocator(&admin)); assert!(client.is_operator(&admin)); // Admin holds all roles implicitly } + +// ============================================================ +// Commitment ID generation and uniqueness tests +// ============================================================ + +/// Two successive `create_commitment` calls produce distinct `commitment_id`s. +#[test] +fn test_commitment_id_uniqueness_across_two_creates() { + let e = Env::default(); + e.mock_all_auths_allowing_non_root_auth(); + // mint 10x so the owner can fund at least 5 creates at `amount` each + let amount = 100i128; + let (_, client, owner, asset_address, _, _, rules) = + setup_create_commitment_fixture(&e, 5 * amount); + + let id1 = client.create_commitment(&owner, &amount, &asset_address, &rules); + let id2 = client.create_commitment(&owner, &amount, &asset_address, &rules); + + assert_ne!(id1, id2); +} + +/// Each generated ID follows the `COMMIT_` format. +#[test] +fn test_commitment_id_format_is_commit_prefix() { + let e = Env::default(); + e.mock_all_auths_allowing_non_root_auth(); + // mint 20x so the owner can fund 5 creates + let amount = 100i128; + let (_, client, owner, asset_address, _, _, rules) = + setup_create_commitment_fixture(&e, 10 * amount); + + for expected_n in 0u64..5u64 { + let id = client.create_commitment(&owner, &amount, &asset_address, &rules); + // Build the expected string "COMMIT_" using the same no_std approach + let mut buf = [0u8; 28]; + buf[0] = b'C'; + buf[1] = b'O'; + buf[2] = b'M'; + buf[3] = b'M'; + buf[4] = b'I'; + buf[5] = b'T'; + buf[6] = b'_'; + let mut n = expected_n; + let mut i = 7usize; + if n == 0 { + buf[i] = b'0'; + i += 1; + } else { + let mut digits = [0u8; 20]; + let mut count = 0usize; + while n > 0 { + digits[count] = (n % 10) as u8 + b'0'; + n /= 10; + count += 1; + } + for j in 0..count { + buf[i] = digits[count - 1 - j]; + i += 1; + } + } + let expected = + String::from_str(&e, core::str::from_utf8(&buf[..i]).unwrap()); + assert_eq!(id, expected, "commitment #{expected_n} had unexpected id"); + } +} + +/// `commitment_id_exists` returns `false` before creation and `true` after. +#[test] +fn test_commitment_id_exists_before_and_after_create() { + let e = Env::default(); + e.mock_all_auths_allowing_non_root_auth(); + let amount = 100i128; + let (_, client, owner, asset_address, _, _, rules) = + setup_create_commitment_fixture(&e, amount); + + let phantom = String::from_str(&e, "COMMIT_0"); + assert!(!client.commitment_id_exists(&phantom)); + + let id = client.create_commitment(&owner, &amount, &asset_address, &rules); + assert_eq!(id, phantom); + assert!(client.commitment_id_exists(&id)); +} + +/// `commitment_id_exists` returns `false` for an ID that was never created. +#[test] +fn test_commitment_id_exists_nonexistent_returns_false() { + let e = Env::default(); + e.mock_all_auths_allowing_non_root_auth(); + let amount = 1_000i128; + let (_, client, _, _, _, _, _) = setup_create_commitment_fixture(&e, amount); + + assert!(!client.commitment_id_exists(&String::from_str(&e, "COMMIT_999"))); +} + +/// Counter monotonically advances: n-th commitment gets ID `COMMIT_n`. +#[test] +fn test_commitment_ids_are_monotonically_increasing() { + let e = Env::default(); + e.mock_all_auths_allowing_non_root_auth(); + let amount = 100i128; + // mint 10x to fund 3 creates + let (_, client, owner, asset_address, _, _, rules) = + setup_create_commitment_fixture(&e, 5 * amount); + + let id0 = client.create_commitment(&owner, &amount, &asset_address, &rules); + let id1 = client.create_commitment(&owner, &amount, &asset_address, &rules); + let id2 = client.create_commitment(&owner, &amount, &asset_address, &rules); + + assert_eq!(id0, String::from_str(&e, "COMMIT_0")); + assert_eq!(id1, String::from_str(&e, "COMMIT_1")); + assert_eq!(id2, String::from_str(&e, "COMMIT_2")); + + // All three must be distinct + assert_ne!(id0, id1); + assert_ne!(id1, id2); + assert_ne!(id0, id2); +} + +/// The commitment stored under each generated ID is retrievable and matches the inputs. +#[test] +fn test_get_commitment_returns_correct_record_for_generated_id() { + let e = Env::default(); + e.mock_all_auths_allowing_non_root_auth(); + let amount = 100i128; + let (_, client, owner, asset_address, _, _, rules) = + setup_create_commitment_fixture(&e, amount); + + let id = client.create_commitment(&owner, &amount, &asset_address, &rules); + let c = client.get_commitment(&id); + + assert_eq!(c.commitment_id, id); + assert_eq!(c.owner, owner); + assert_eq!(c.asset_address, asset_address); + assert_eq!(c.status, String::from_str(&e, "active")); +} diff --git a/contracts/commitment_core/test_snapshots/tests/test_allocate_event.1.json b/contracts/commitment_core/test_snapshots/tests/test_allocate_event.1.json index aa253808..9663e6b7 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_allocate_event.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_allocate_event.1.json @@ -80,18 +80,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ diff --git a/contracts/commitment_core/test_snapshots/tests/test_create_commitment_event.1.json b/contracts/commitment_core/test_snapshots/tests/test_create_commitment_event.1.json index 6303a562..850b99d0 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_create_commitment_event.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_create_commitment_event.1.json @@ -1,21 +1,233 @@ { "generators": { - "address": 4, + "address": 6, "nonce": 0 }, "auth": [ - [] + [ + [ + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF", + { + "function": { + "contract_fn": { + "contract_address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "function_name": "set_admin", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + { + "function": { + "contract_fn": { + "contract_address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "function_name": "mint", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 2000 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "create_commitment", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + }, + { + "address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN" + }, + { + "map": [ + { + "key": { + "symbol": "commitment_type" + }, + "val": { + "string": "balanced" + } + }, + { + "key": { + "symbol": "duration_days" + }, + "val": { + "u32": 30 + } + }, + { + "key": { + "symbol": "early_exit_penalty" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "grace_period_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "max_loss_percent" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "min_fee_threshold" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ] ], "ledger": { "protocol_version": 21, "sequence_number": 0, - "timestamp": 0, + "timestamp": 1700000000, "network_id": "0000000000000000000000000000000000000000000000000000000000000000", "base_reserve": 0, "min_persistent_entry_ttl": 4096, "min_temp_entry_ttl": 16, "max_entry_ttl": 6312000, "ledger_entries": [ + [ + { + "account": { + "account_id": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "account": { + "account_id": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF", + "balance": 0, + "seq_num": 0, + "num_sub_entries": 0, + "inflation_dest": null, + "flags": 0, + "home_domain": "", + "thresholds": "01010101", + "signers": [], + "ext": "v0" + } + }, + "ext": "v0" + }, + null + ] + ], + [ + { + "contract_data": { + "contract": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], [ { "contract_data": { @@ -76,19 +288,165 @@ ] }, "val": { - "vec": [] + "vec": [ + { + "string": "COMMIT_0" + } + ] } }, { "key": { "vec": [ { - "symbol": "AuthorizedUpdaters" + "symbol": "Commitment" + }, + { + "string": "COMMIT_0" } ] }, "val": { - "vec": [] + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + }, + { + "key": { + "symbol": "asset_address" + }, + "val": { + "address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN" + } + }, + { + "key": { + "symbol": "commitment_id" + }, + "val": { + "string": "COMMIT_0" + } + }, + { + "key": { + "symbol": "created_at" + }, + "val": { + "u64": 1700000000 + } + }, + { + "key": { + "symbol": "current_value" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + }, + { + "key": { + "symbol": "expires_at" + }, + "val": { + "u64": 1702592000 + } + }, + { + "key": { + "symbol": "nft_token_id" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "owner" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + }, + { + "key": { + "symbol": "rules" + }, + "val": { + "map": [ + { + "key": { + "symbol": "commitment_type" + }, + "val": { + "string": "balanced" + } + }, + { + "key": { + "symbol": "duration_days" + }, + "val": { + "u32": 30 + } + }, + { + "key": { + "symbol": "early_exit_penalty" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "grace_period_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "max_loss_percent" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "min_fee_threshold" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "string": "active" + } + } + ] } }, { @@ -100,7 +458,26 @@ ] }, "val": { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "OwnerCommitments" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "vec": [ + { + "string": "COMMIT_0" + } + ] } }, { @@ -124,7 +501,7 @@ ] }, "val": { - "u64": 0 + "u64": 1 } }, { @@ -138,7 +515,7 @@ "val": { "i128": { "hi": 0, - "lo": 0 + "lo": 1000 } } } @@ -154,101 +531,904 @@ ], [ { - "contract_code": { - "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": "ledger_key_contract_instance", + "durability": "persistent" } }, [ { "last_modified_ledger_seq": 0, "data": { - "contract_code": { + "contract_data": { "ext": "v0", - "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "code": "" + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } } }, "ext": "v0" }, 4095 ] - ] - ] - }, - "events": [ - { - "event": { - "ext": "v0", - "contract_id": null, - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_call" - }, - { - "bytes": "0000000000000000000000000000000000000000000000000000000000000001" - }, - { - "symbol": "initialize" + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 } - ], + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, "data": { - "vec": [ - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } }, - { - "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" - } - ] - } - } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", - "type_": "contract", - "body": { - "v0": { - "topics": [ - { - "symbol": "EmgMode" + "durability": "temporary", + "val": "void" } - ], + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "key": { "vec": [ { - "symbol": "EMG_OFF" + "symbol": "Balance" }, { - "u64": 0 + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" } ] - } + }, + "durability": "persistent" } - } - }, - "failed_call": false - }, - { - "event": { - "ext": "v0", - "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", - "type_": "diagnostic", - "body": { - "v0": { - "topics": [ - { - "symbol": "fn_return" - }, - { - "symbol": "initialize" + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": "stellar_asset", + "storage": [ + { + "key": { + "symbol": "METADATA" + }, + "val": { + "map": [ + { + "key": { + "symbol": "decimal" + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF" + } + }, + { + "key": { + "symbol": "symbol" + }, + "val": { + "string": "aaa" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "AssetInfo" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "AlphaNum4" + }, + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "aaa\\0" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "bytes": "0000000000000000000000000000000000000000000000000000000000000006" + } + } + ] + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 120960 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0" + }, + { + "symbol": "init_asset" + } + ], + "data": { + "bytes": "0000000161616100000000000000000000000000000000000000000000000000000000000000000000000006" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "init_asset" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0" + }, + { + "symbol": "set_admin" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "set_admin" + }, + { + "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_admin" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0" + }, + { + "symbol": "mint" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 2000 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "mint" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 2000 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "mint" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "initialize" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "EmgMode" + } + ], + "data": { + "vec": [ + { + "symbol": "EMG_OFF" + }, + { + "u64": 1700000000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "initialize" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "create_commitment" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + }, + { + "address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN" + }, + { + "map": [ + { + "key": { + "symbol": "commitment_type" + }, + "val": { + "string": "balanced" + } + }, + { + "key": { + "symbol": "duration_days" + }, + "val": { + "u32": 30 + } + }, + { + "key": { + "symbol": "early_exit_penalty" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "grace_period_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "max_loss_percent" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "min_fee_threshold" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0" + }, + { + "symbol": "balance" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "balance" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 2000 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0" + }, + { + "symbol": "transfer" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "transfer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANHUF" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "04cadb4a570fd2e4652e814101509912cce6c9a2325d6eec8d7100caf859f3e0", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "transfer" } ], "data": "void" @@ -256,6 +1436,203 @@ } }, "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000002" + }, + { + "symbol": "mint" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "string": "COMMIT_0" + }, + { + "u32": 30 + }, + { + "u32": 10 + }, + { + "string": "balanced" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + }, + { + "address": "CACMVW2KK4H5FZDFF2AUCAKQTEJMZZWJUIZF23XMRVYQBSXYLHZ6BKWN" + }, + { + "u32": 10 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000002", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "mint" + } + ], + "data": { + "u32": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "Created" + }, + { + "string": "COMMIT_0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ], + "data": { + "vec": [ + { + "i128": { + "hi": 0, + "lo": 1000 + } + }, + { + "map": [ + { + "key": { + "symbol": "commitment_type" + }, + "val": { + "string": "balanced" + } + }, + { + "key": { + "symbol": "duration_days" + }, + "val": { + "u32": 30 + } + }, + { + "key": { + "symbol": "early_exit_penalty" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "grace_period_days" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "max_loss_percent" + }, + "val": { + "u32": 10 + } + }, + { + "key": { + "symbol": "min_fee_threshold" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + ] + }, + { + "u32": 1 + }, + { + "u64": 1700000000 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "create_commitment" + } + ], + "data": { + "string": "COMMIT_0" + } + } + } + }, + "failed_call": false } ] } \ No newline at end of file diff --git a/contracts/commitment_core/test_snapshots/tests/test_get_admin.1.json b/contracts/commitment_core/test_snapshots/tests/test_get_admin.1.json index 3c5ff991..50cae5b2 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_get_admin.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_get_admin.1.json @@ -80,18 +80,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ diff --git a/contracts/commitment_core/test_snapshots/tests/test_get_nft_contract.1.json b/contracts/commitment_core/test_snapshots/tests/test_get_nft_contract.1.json index 3c5ff991..50cae5b2 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_get_nft_contract.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_get_nft_contract.1.json @@ -80,18 +80,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ diff --git a/contracts/commitment_core/test_snapshots/tests/test_get_owner_commitments.1.json b/contracts/commitment_core/test_snapshots/tests/test_get_owner_commitments.1.json index e902d51a..75cefd81 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_get_owner_commitments.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_get_owner_commitments.1.json @@ -80,18 +80,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ diff --git a/contracts/commitment_core/test_snapshots/tests/test_get_total_commitments.1.json b/contracts/commitment_core/test_snapshots/tests/test_get_total_commitments.1.json index 3c5ff991..50cae5b2 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_get_total_commitments.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_get_total_commitments.1.json @@ -80,18 +80,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ diff --git a/contracts/commitment_core/test_snapshots/tests/test_initialize.1.json b/contracts/commitment_core/test_snapshots/tests/test_initialize.1.json index acd2a443..278680a6 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_initialize.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_initialize.1.json @@ -79,18 +79,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ diff --git a/contracts/commitment_core/test_snapshots/tests/test_update_value_event.1.json b/contracts/commitment_core/test_snapshots/tests/test_update_value_event.1.json index 7cd09c25..d0434ae0 100644 --- a/contracts/commitment_core/test_snapshots/tests/test_update_value_event.1.json +++ b/contracts/commitment_core/test_snapshots/tests/test_update_value_event.1.json @@ -5,7 +5,34 @@ }, "auth": [ [], - [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "update_value", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "test_id" + }, + { + "i128": { + "hi": 0, + "lo": 1100 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + ], [], [] ], @@ -82,18 +109,6 @@ "vec": [] } }, - { - "key": { - "vec": [ - { - "symbol": "AuthorizedUpdaters" - } - ] - }, - "val": { - "vec": [] - } - }, { "key": { "vec": [ @@ -309,6 +324,39 @@ 4095 ] ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], [ { "contract_code": { @@ -380,6 +428,9 @@ ], "data": { "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, { "string": "test_id" }, diff --git a/contracts/shared_utils/src/lib.rs b/contracts/shared_utils/src/lib.rs index 3f8080b4..762b00c4 100644 --- a/contracts/shared_utils/src/lib.rs +++ b/contracts/shared_utils/src/lib.rs @@ -38,4 +38,10 @@ pub use batch::{ BatchResultString, BatchResultVoid, DetailedBatchError, RollbackHelper, StateSnapshot, }; pub use emergency::EmergencyControl; +pub use error_codes::emit_error_event; +pub use math::SafeMath; +pub use pausable::Pausable; +pub use rate_limiting::RateLimiter; +pub use time::TimeUtils; +pub use validation::Validation; diff --git a/docs/KNOWN_LIMITATIONS.md b/docs/KNOWN_LIMITATIONS.md index e5761531..7ab70354 100644 --- a/docs/KNOWN_LIMITATIONS.md +++ b/docs/KNOWN_LIMITATIONS.md @@ -1,7 +1,7 @@ # Known Limitations -- commitment*core::generate_commitment_id returns a constant prefix ("commitment*") and does not guarantee uniqueness. -- commitment_core::update_value emits an event but does not persist the new value. +- ~~commitment_core::generate_commitment_id returns a constant prefix ("commitment_") and does not guarantee uniqueness.~~ **Fixed**: IDs are now generated as `COMMIT_` using a monotonically increasing `TotalCommitments` counter. A duplicate-ID guard (`DuplicateCommitmentId` error) is enforced before every write. A `commitment_id_exists()` query function is exposed for off-chain validation. +- ~~commitment_core::update_value emits an event but does not persist the new value.~~ **Fixed**: `update_value` now persists `current_value` and status to storage before emitting the event. - commitment_core state-changing functions (create_commitment, settle, early_exit, allocate, update_value) do not enforce `require_auth`. - commitment_nft::mint does not enforce an authorized minter list (DataKey::AuthorizedMinter is unused). - commitment_nft::settle is not restricted to the core contract.