From 80a5abbd6fb3057d4bec0268b751ae553e4b5cca Mon Sep 17 00:00:00 2001 From: Mansur Azatbek Date: Fri, 3 Apr 2026 10:45:12 +0500 Subject: [PATCH 1/5] chore: minor updates --- Cargo.lock | 452 +++++++----------- Cargo.toml | 3 +- .../awesome-sails/access-control/src/lib.rs | 15 +- crates/awesome-sails/msg-tracker/src/lib.rs | 3 +- .../awesome-sails/msg-tracker/src/storage.rs | 3 +- crates/awesome-sails/vft-admin/src/lib.rs | 5 +- crates/awesome-sails/vft-metadata/src/lib.rs | 3 +- .../vft-native-exchange-admin/src/lib.rs | 5 +- crates/awesome-sails/vft/src/lib.rs | 3 +- utils/Cargo.toml | 1 + utils/src/error.rs | 11 +- 11 files changed, 194 insertions(+), 310 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 803b8f3..81741bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,7 +31,7 @@ name = "access-control-test-client" version = "0.1.0" dependencies = [ "access-control-test-app", - "mockall 0.14.0", + "mockall", "sails-rs", ] @@ -374,12 +374,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "ascii-canvas" -version = "3.0.0" +name = "askama" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b8246bcbf8eb97abef10c2d92166449680d41d55c0fc6978a91dec2e3619608" +dependencies = [ + "askama_macros", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +checksum = "2f9670bc84a28bb3da91821ef74226949ab63f1265aff7c751634f1dd0e6f97c" dependencies = [ - "term", + "askama_parser", + "memchr", + "proc-macro2", + "quote", + "rustc-hash 2.1.2", + "syn 2.0.117", +] + +[[package]] +name = "askama_macros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0756b45480437dded0565dfc568af62ccce146fb6cfe902e808ba86e445f44f" +dependencies = [ + "askama_derive", +] + +[[package]] +name = "askama_parser" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0af3691ba3af77949c0b5a3925444b85cb58a0184cc7fec16c68ba2e7be868" +dependencies = [ + "rustc-hash 2.1.2", + "unicode-ident", + "winnow 1.0.1", ] [[package]] @@ -485,7 +523,7 @@ name = "awesome-sails-test-client" version = "0.1.0" dependencies = [ "awesome-sails-test-app", - "mockall 0.14.0", + "mockall", "sails-rs", ] @@ -501,6 +539,7 @@ dependencies = [ "parity-scale-codec", "primitive-types 0.12.2", "proptest", + "sails-reflect-hash", "scale-info", "thiserror 2.0.17", ] @@ -611,12 +650,6 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - [[package]] name = "bincode" version = "1.3.3" @@ -641,35 +674,20 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.117", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec 0.6.3", -] - [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit-vec" version = "0.8.0" @@ -1167,18 +1185,18 @@ checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] [[package]] name = "convert_case" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" dependencies = [ "unicode-segmentation", ] @@ -1583,27 +1601,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -1789,15 +1786,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ena" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" -dependencies = [ - "log", -] - [[package]] name = "enum-iterator" version = "0.7.0" @@ -1997,12 +1985,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" version = "1.1.9" @@ -2025,6 +2007,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -2677,9 +2665,9 @@ dependencies = [ [[package]] name = "genco" -version = "0.17.10" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35958104272e516c2a5f66a9d82fba4784d2b585fc1e2358b8f96e15d342995" +checksum = "77ab846431e5d637791b3279e7242fe2b21e11c3d8b4cf6a99f645c5f16ba7c0" dependencies = [ "genco-macros", "relative-path", @@ -2688,9 +2676,9 @@ dependencies = [ [[package]] name = "genco-macros" -version = "0.17.10" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43eaff6bbc0b3a878361aced5ec6a2818ee7c541c5b33b5880dfa9a86c23e9e7" +checksum = "c42a1fe5a699c7f1d36ea6e04ed680a5c787cabff4b610ae3b8954ea3bcefec1" dependencies = [ "proc-macro2", "quote", @@ -2951,20 +2939,6 @@ dependencies = [ "tracing-subscriber 0.3.22", ] -[[package]] -name = "handlebars" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" -dependencies = [ - "log", - "pest", - "pest_derive", - "serde", - "serde_json", - "thiserror 1.0.69", -] - [[package]] name = "hash-db" version = "0.16.0" @@ -3013,7 +2987,8 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", + "serde", ] [[package]] @@ -3021,6 +2996,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -3398,36 +3378,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" -[[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set 0.5.3", - "ena", - "itertools 0.11.0", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax 0.8.10", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" -dependencies = [ - "regex-automata 0.4.14", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -3591,38 +3541,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "logos" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" -dependencies = [ - "logos-derive", -] - -[[package]] -name = "logos-codegen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn 2.0.117", -] - -[[package]] -name = "logos-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" -dependencies = [ - "logos-codegen", -] - [[package]] name = "mach" version = "0.3.2" @@ -3831,21 +3749,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mockall" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" -dependencies = [ - "cfg-if", - "downcast", - "fragile", - "lazy_static", - "mockall_derive 0.12.1", - "predicates", - "predicates-tree", -] - [[package]] name = "mockall" version = "0.14.0" @@ -3855,23 +3758,11 @@ dependencies = [ "cfg-if", "downcast", "fragile", - "mockall_derive 0.14.0", + "mockall_derive", "predicates", "predicates-tree", ] -[[package]] -name = "mockall_derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "mockall_derive" version = "0.14.0" @@ -3907,7 +3798,7 @@ dependencies = [ name = "msg-tracker-test-client" version = "0.1.0" dependencies = [ - "mockall 0.14.0", + "mockall", "msg-tracker-test-app", "sails-rs", ] @@ -3953,12 +3844,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "nix" version = "0.26.4" @@ -4338,25 +4223,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.13.0", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -4446,12 +4312,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "predicates" version = "3.1.4" @@ -4602,8 +4462,8 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", + "bit-set", + "bit-vec", "bitflags 2.11.0", "num-traits", "rand 0.9.2", @@ -4813,17 +4673,6 @@ dependencies = [ "bitflags 2.11.0", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 1.0.69", -] - [[package]] name = "ref-cast" version = "1.0.25" @@ -4967,6 +4816,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -5068,59 +4923,66 @@ dependencies = [ [[package]] name = "sails-client-gen" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dddd93868445aa99aa3fe50342ada5ba1264f5fe7b6d5b43130f4f45c2ebf0b2" +version = "1.0.0-beta.2" dependencies = [ "anyhow", - "convert_case 0.7.1", + "convert_case 0.11.0", "genco", "parity-scale-codec", - "sails-idl-parser", + "sails-idl-meta", + "sails-idl-parser-v2", +] + +[[package]] +name = "sails-idl-embed" +version = "1.0.0-beta.2" +dependencies = [ + "flate2", + "tempfile", + "thiserror 2.0.17", + "wasm-encoder 0.230.0", + "wasmparser 0.230.0", ] [[package]] name = "sails-idl-gen" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea7b9ced73a79650df1d8c1bfa506f4e08cd77a4cef264258e5f0b04d910a835" +version = "1.0.0-beta.2" dependencies = [ - "convert_case 0.7.1", + "askama", + "convert_case 0.11.0", "gprimitives", - "handlebars", + "quote", "sails-idl-meta", "scale-info", - "serde", - "serde_json", + "syn 2.0.117", "thiserror 2.0.17", ] [[package]] name = "sails-idl-meta" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a22a7f17abed84627afdb89ef58a55e07ecf34e54665dab3157189b86f3a9c" +version = "1.0.0-beta.2" dependencies = [ + "askama", + "keccak-const", + "parity-scale-codec", "scale-info", ] [[package]] -name = "sails-idl-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c0dd7750fbc0e29c888a03c22d057c7ce046077d9a574e875c5aaa83aecb00" +name = "sails-idl-parser-v2" +version = "1.0.0-beta.2" dependencies = [ - "lalrpop", - "lalrpop-util", - "logos", + "paste", + "pest", + "pest_derive", + "sails-idl-meta", + "tempfile", "thiserror 2.0.17", ] [[package]] name = "sails-macros" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bdd89a1e06800a39c9ec894862579d9698080fcc259cbae0aa5a9f1c5ab532c" +version = "1.0.0-beta.2" dependencies = [ "proc-macro-error", "sails-macros-core", @@ -5128,11 +4990,9 @@ dependencies = [ [[package]] name = "sails-macros-core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0108a50192e715db43ba94d3fcffaf0904c255e9f2b045950cec4325d2970b7e" +version = "1.0.0-beta.2" dependencies = [ - "convert_case 0.7.1", + "convert_case 0.11.0", "parity-scale-codec", "proc-macro-error", "proc-macro2", @@ -5140,13 +5000,30 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "sails-reflect-hash" +version = "1.0.0-beta.2" +dependencies = [ + "gprimitives", + "keccak-const", + "sails-reflect-hash-derive", +] + +[[package]] +name = "sails-reflect-hash-derive" +version = "1.0.0-beta.2" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "sails-rs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3f3db39d829e94899ade845992761597ca504e87db9b7f57141886f0117077" +version = "1.0.0-beta.2" dependencies = [ - "convert_case 0.7.1", + "convert_case 0.11.0", "futures", "gcore", "gear-core", @@ -5154,20 +5031,24 @@ dependencies = [ "gear-wasm-builder", "gprimitives", "gstd", + "gsys", "gtest", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "hex", + "keccak-const", "log", - "mockall 0.12.1", + "mockall", "parity-scale-codec", "paste", "pin-project-lite", "sails-client-gen", + "sails-idl-embed", "sails-idl-gen", "sails-idl-meta", "sails-macros", + "sails-reflect-hash", "scale-info", - "spin", + "spin 0.10.0", "thiserror 2.0.17", "tokio-stream", ] @@ -5580,12 +5461,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "620a1d43d70e142b1d46a929af51d44f383db9c7a2ec122de2cd992ccfcf3c18" -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - [[package]] name = "slab" version = "0.4.12" @@ -6132,6 +6007,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + [[package]] name = "spki" version = "0.7.3" @@ -6186,18 +6067,6 @@ dependencies = [ "serde", ] -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "strsim" version = "0.11.1" @@ -6281,9 +6150,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.4.1", @@ -6292,17 +6161,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -7148,7 +7006,7 @@ dependencies = [ "arrayvec", "multi-stash", "smallvec", - "spin", + "spin 0.9.8", "wasmi_collections", "wasmi_core 0.38.0", "wasmi_ir", @@ -7235,7 +7093,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags 2.11.0", + "hashbrown 0.15.5", "indexmap 2.13.0", + "semver", + "serde", ] [[package]] @@ -7744,6 +7605,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index a3ab646..8db5d08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,8 @@ members = [ [workspace.dependencies] -sails-rs = { version = "0.10.1", default-features = false, features = ["gstd"] } +sails-rs = { path = "../sails/rs", default-features = false, features = ["gstd"] } +sails-reflect-hash = { path = "../sails/rs/reflect-hash", default-features = false } awesome-sails-utils = { path = "utils", version = "0.2.0", default-features = false } gtest = "=1.10.0" gprimitives = { version = "1.10.0", default-features = false } diff --git a/crates/awesome-sails/access-control/src/lib.rs b/crates/awesome-sails/access-control/src/lib.rs index 70bc369..55a2fa9 100644 --- a/crates/awesome-sails/access-control/src/lib.rs +++ b/crates/awesome-sails/access-control/src/lib.rs @@ -46,6 +46,7 @@ use crate::error::{AccessDenied, EmitError, Error, NotAccountOwner}; use awesome_sails_storage::{InfallibleStorageMut, StorageRefCell}; use core::marker::PhantomData; use sails_rs::{ + ReflectHash, collections::{BTreeMap, BTreeSet}, prelude::*, }; @@ -70,9 +71,10 @@ pub struct RoleData { } /// Pagination parameters for listing roles or members. -#[derive(Encode, Decode, TypeInfo, Debug, Clone, Copy)] +#[derive(Encode, Decode, TypeInfo, Debug, Clone, Copy, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. pub offset: u32, @@ -577,9 +579,10 @@ impl<'a, S: InfallibleStorageMut> AccessControl<'a, S> { /// Events emitted by the Access Control service. #[event] -#[derive(Clone, Debug, PartialEq, Encode, TypeInfo)] +#[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when `target_account` is granted `role_id`. RoleGranted { @@ -607,26 +610,28 @@ pub mod error { use crate::RoleId; pub use awesome_sails_utils::error::{BadOrigin, EmitError, Error}; use sails_rs::{ - ActorId, + ActorId, ReflectHash, scale_codec::{Decode, Encode}, scale_info::TypeInfo, }; /// Error indicating access was denied due to missing role permissions. - #[derive(Clone, Debug, Decode, Encode, TypeInfo, thiserror::Error)] + #[derive(Clone, Debug, Decode, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] #[error("Access denied: account {account_id:?} does not have role {role_id:?}")] #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] pub struct AccessDenied { pub account_id: ActorId, pub role_id: RoleId, } /// Error indicating that an operation required the caller to be the account owner, but they were not. - #[derive(Clone, Debug, Decode, Encode, TypeInfo, thiserror::Error)] + #[derive(Clone, Debug, Decode, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] #[error("Not account owner: account {account_id:?}, message source {message_source:?}")] #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] pub struct NotAccountOwner { pub account_id: ActorId, pub message_source: ActorId, diff --git a/crates/awesome-sails/msg-tracker/src/lib.rs b/crates/awesome-sails/msg-tracker/src/lib.rs index 5100159..1982be8 100644 --- a/crates/awesome-sails/msg-tracker/src/lib.rs +++ b/crates/awesome-sails/msg-tracker/src/lib.rs @@ -75,9 +75,10 @@ where } /// Pagination parameters for listing tracked messages. -#[derive(Clone, Copy, Debug, Decode, Encode, TypeInfo)] +#[derive(Clone, Copy, Debug, Decode, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. pub offset: u32, diff --git a/crates/awesome-sails/msg-tracker/src/storage.rs b/crates/awesome-sails/msg-tracker/src/storage.rs index 99d0d0e..1fb149e 100644 --- a/crates/awesome-sails/msg-tracker/src/storage.rs +++ b/crates/awesome-sails/msg-tracker/src/storage.rs @@ -177,9 +177,10 @@ impl MessageStorage for FixedStorage { pub mod error { use sails_rs::prelude::*; - #[derive(Debug, Decode, Encode, TypeInfo, thiserror::Error)] + #[derive(Debug, Decode, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] pub enum TrackerError { /// Indicates that the fixed storage capacity has been exceeded. #[error("Capacity exceeded")] diff --git a/crates/awesome-sails/vft-admin/src/lib.rs b/crates/awesome-sails/vft-admin/src/lib.rs index c5dda0a..7363f1d 100644 --- a/crates/awesome-sails/vft-admin/src/lib.rs +++ b/crates/awesome-sails/vft-admin/src/lib.rs @@ -360,11 +360,12 @@ impl< } } -/// Events emitted by the VFT Admin service. +/// Events emitted by the VFT admin service. #[event] -#[derive(Clone, Debug, PartialEq, Encode, TypeInfo)] +#[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when a burn operation occurs. BurnerTookPlace, diff --git a/crates/awesome-sails/vft-metadata/src/lib.rs b/crates/awesome-sails/vft-metadata/src/lib.rs index caa26eb..6794528 100644 --- a/crates/awesome-sails/vft-metadata/src/lib.rs +++ b/crates/awesome-sails/vft-metadata/src/lib.rs @@ -93,9 +93,10 @@ impl> VftMetadata { } /// Represents the metadata of a VFT: name, symbol, and decimals. -#[derive(Debug, Clone, Encode, Decode, TypeInfo, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Encode, Decode, TypeInfo, ReflectHash, PartialEq, Eq, PartialOrd, Ord)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub struct Metadata { name: String, symbol: String, diff --git a/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs b/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs index a2170bc..16b825d 100644 --- a/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs +++ b/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs @@ -120,11 +120,12 @@ where } } -/// Events emitted by the VFT Native Exchange Admin service. +/// Events emitted by the VFT native exchange admin service. #[event] -#[derive(Clone, Debug, PartialEq, Encode, TypeInfo)] +#[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when re-minting tokens after a failed transfer fails. FailedMint { to: ActorId, value: U256 }, diff --git a/crates/awesome-sails/vft/src/lib.rs b/crates/awesome-sails/vft/src/lib.rs index ada235e..e5fae8a 100644 --- a/crates/awesome-sails/vft/src/lib.rs +++ b/crates/awesome-sails/vft/src/lib.rs @@ -239,9 +239,10 @@ impl, B: StorageMut> Vft<'_, A /// Events emitted by the VFT service. #[event] -#[derive(Clone, Debug, PartialEq, Encode, TypeInfo)] +#[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when an approval is granted or updated. Approval { diff --git a/utils/Cargo.toml b/utils/Cargo.toml index 30c54f7..3d9b56f 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -22,6 +22,7 @@ derive_more = { workspace = true, features = [ "deref", ] } thiserror = { workspace = true } +sails-reflect-hash = { workspace = true } awesome-sails-storage = { workspace = true } hashbrown = { workspace = true, features = ["ahash"] } bnum = { version = "0.13.0", default-features = false } diff --git a/utils/src/error.rs b/utils/src/error.rs index a65007a..9c22de7 100644 --- a/utils/src/error.rs +++ b/utils/src/error.rs @@ -25,12 +25,13 @@ use alloc::string::{String, ToString}; use core::fmt; use parity_scale_codec::{Decode, Encode}; +use sails_reflect_hash::ReflectHash; use scale_info::TypeInfo; /// Represents a generic error type within the `awesome-sails` ecosystem. /// /// This struct wraps a string message providing details about the error. -#[derive(Clone, Decode, Encode, TypeInfo, derive_more::Display)] +#[derive(Clone, Decode, Encode, TypeInfo, ReflectHash, derive_more::Display)] #[codec(crate = parity_scale_codec)] #[scale_info(crate = scale_info)] #[display("{}", _0)] @@ -66,7 +67,7 @@ impl From for Error { /// Indicates an incorrect input argument was provided. /// /// This error is typically returned when an argument does not meet the expected criteria. -#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, thiserror::Error)] +#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("incorrect input argument")] #[scale_info(crate = scale_info)] @@ -76,7 +77,7 @@ pub struct BadInput; /// /// This error is typically returned when the message sender does not have the required permissions /// or is not the expected entity. -#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, thiserror::Error)] +#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("incorrect message origin")] #[scale_info(crate = scale_info)] @@ -85,7 +86,7 @@ pub struct BadOrigin; /// Indicates an incorrect value was attached to the message. /// /// This error is typically returned when the transferred value is insufficient or invalid for the operation. -#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, thiserror::Error)] +#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("incorrect message value")] #[scale_info(crate = scale_info)] @@ -94,7 +95,7 @@ pub struct BadValue; /// Indicates a failure occurred while attempting to emit an event. /// /// This error is typically returned when the event emission mechanism encounters an issue. -#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, thiserror::Error)] +#[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("emit event error")] #[scale_info(crate = scale_info)] From 08880fd256092767060618e43c7c0edc669e8e45 Mon Sep 17 00:00:00 2001 From: Mansur Azatbek Date: Fri, 3 Apr 2026 11:41:34 +0500 Subject: [PATCH 2/5] fix tests --- Cargo.lock | 8 +- .../app/tests/common/mod.rs | 20 +- tests/access-control-test/app/tests/gtest.rs | 114 +- .../client/access_control_test_client.idl | 311 ++--- .../client/src/access_control_test_client.rs | 342 +++--- .../app/tests/common/mod.rs | 23 +- tests/awesome-sails-test/app/tests/gtest.rs | 169 +-- .../client/awesome_sails_test_client.idl | 1076 +++++++++-------- .../client/src/awesome_sails_test_client.rs | 901 ++++++++------ tests/msg-tracker-test/app/src/lib.rs | 6 +- .../msg-tracker-test/app/tests/common/mod.rs | 18 +- tests/msg-tracker-test/app/tests/gtest.rs | 71 +- .../client/msg_tracker_test_client.idl | 107 +- .../client/src/msg_tracker_test_client.rs | 222 ++-- 14 files changed, 1940 insertions(+), 1448 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81741bf..b9d5c86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4940,8 +4940,8 @@ dependencies = [ "flate2", "tempfile", "thiserror 2.0.17", - "wasm-encoder 0.230.0", - "wasmparser 0.230.0", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", ] [[package]] @@ -7093,10 +7093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags 2.11.0", - "hashbrown 0.15.5", "indexmap 2.13.0", - "semver", - "serde", ] [[package]] @@ -7109,6 +7106,7 @@ dependencies = [ "hashbrown 0.15.5", "indexmap 2.13.0", "semver", + "serde", ] [[package]] diff --git a/tests/access-control-test/app/tests/common/mod.rs b/tests/access-control-test/app/tests/common/mod.rs index 113ed05..9996590 100644 --- a/tests/access-control-test/app/tests/common/mod.rs +++ b/tests/access-control-test/app/tests/common/mod.rs @@ -19,7 +19,7 @@ use access_control_test_client::{AccessControlTestClientCtors, AccessControlTestClientProgram}; use sails_rs::{ ActorId, - client::{Actor, GearEnv, GtestEnv, GtestError}, + client::{Actor, GearEnv, GtestEnv}, gtest::System, prelude::*, }; @@ -81,25 +81,9 @@ pub async fn deploy_program() -> ( .deploy::(code_id, b"salt".to_vec()) .new() .await - .expect("failed to deploy program"); + .unwrap(); let program_id = program.id(); (program, env, program_id) } - -#[track_caller] -pub fn assert_str_panic(e: GtestError, exp: &str) { - match e { - GtestError::ReplyHasError( - ErrorReplyReason::Execution(SimpleExecutionError::UserspacePanic), - res, - ) => { - let actual = String::from_utf8_lossy(&res); - let expected = - format!("panicked with 'called `Result::unwrap()` on an `Err` value: {exp}'"); - assert_eq!(actual, expected); - } - _ => core::panic!("not an expected error reply type: {e:?}"), - } -} diff --git a/tests/access-control-test/app/tests/gtest.rs b/tests/access-control-test/app/tests/gtest.rs index 8254696..2f6377f 100644 --- a/tests/access-control-test/app/tests/gtest.rs +++ b/tests/access-control-test/app/tests/gtest.rs @@ -19,12 +19,14 @@ mod common; use access_control_test_client::{ - AccessControlTestClient, Pagination, - access_control::{AccessControl, events::AccessControlEvents}, + AccessControlTestClient, + access_control::{ + AccessControl, Error as AccessControlError, Pagination, events::AccessControlEvents, + }, }; use awesome_sails::access_control::{DEFAULT_ADMIN_ROLE, RoleId}; use awesome_sails_utils::assert_ok; -use common::{ALICE, BOB, CHARLIE, DAVE, assert_str_panic, deploy_program}; +use common::{ALICE, BOB, CHARLIE, DAVE, deploy_program}; use futures::StreamExt; use sails_rs::prelude::*; @@ -62,7 +64,8 @@ async fn grant_and_revoke_role_success() { .grant_role(MINTER_ROLE, BOB) .with_actor_id(ALICE) .await - .expect("Failed to grant MINTER_ROLE to Bob"); + .expect("Failed to grant MINTER_ROLE to Bob") + .unwrap(); let (actor, event) = events.next().await.unwrap(); assert_eq!(actor, pid); @@ -84,7 +87,8 @@ async fn grant_and_revoke_role_success() { .revoke_role(MINTER_ROLE, BOB) .with_actor_id(ALICE) .await - .expect("Failed to revoke MINTER_ROLE from Bob"); + .expect("Failed to revoke MINTER_ROLE from Bob") + .unwrap(); let (actor, event) = events.next().await.unwrap(); assert_eq!(actor, pid); @@ -111,10 +115,15 @@ async fn grant_role_fail_unauthorized() { let res = access_control_service .grant_role(MINTER_ROLE, DAVE) .with_actor_id(CHARLIE) - .await; - assert_str_panic( - res.unwrap_err(), - "Access denied: account 0x0000000000000000000000002c00000000000000000000000000000000000000 does not have role [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + .await + .unwrap(); + let err = res.unwrap_err(); + assert_eq!( + err.0, + format!( + "Access denied: account {:?} does not have role {:?}", + CHARLIE, DEFAULT_ADMIN_ROLE + ) ); // Dave should not have MINTER_ROLE @@ -132,16 +141,22 @@ async fn revoke_role_fail_unauthorized() { .grant_role(MINTER_ROLE, BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); // Charlie tries to revoke MINTER_ROLE from Bob (unauthorized) let res = access_control_service .revoke_role(MINTER_ROLE, BOB) .with_actor_id(CHARLIE) - .await; - assert_str_panic( - res.unwrap_err(), - "Access denied: account 0x0000000000000000000000002c00000000000000000000000000000000000000 does not have role [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + .await + .unwrap(); + let err = res.unwrap_err(); + assert_eq!( + err.0, + format!( + "Access denied: account {:?} does not have role {:?}", + CHARLIE, DEFAULT_ADMIN_ROLE + ) ); // Bob should still have MINTER_ROLE @@ -161,6 +176,7 @@ async fn renounce_role_success() { .grant_role(PAUSER_ROLE, CHARLIE) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event @@ -169,7 +185,8 @@ async fn renounce_role_success() { .renounce_role(PAUSER_ROLE, CHARLIE) .with_actor_id(CHARLIE) .await - .expect("Failed for Charlie to renounce PAUSER_ROLE"); + .expect("Failed for Charlie to renounce PAUSER_ROLE") + .unwrap(); let (actor, event) = events.next().await.unwrap(); assert_eq!(actor, pid); @@ -197,16 +214,22 @@ async fn renounce_role_fail_other_account() { .grant_role(PAUSER_ROLE, CHARLIE) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); // Bob tries to renounce PAUSER_ROLE for Charlie (unauthorized) let res = access_control_service .renounce_role(PAUSER_ROLE, CHARLIE) .with_actor_id(BOB) - .await; - assert_str_panic( - res.unwrap_err(), - "Not account owner: account 0x0000000000000000000000002c00000000000000000000000000000000000000, message source 0x0000000000000000000000002b00000000000000000000000000000000000000", + .await + .unwrap(); + let err = res.unwrap_err(); + assert_eq!( + err.0, + format!( + "Not account owner: account {:?}, message source {:?}", + CHARLIE, BOB + ) ); // Charlie should still have PAUSER_ROLE @@ -230,6 +253,7 @@ async fn set_role_admin_success() { .grant_role(MODERATOR_ROLE, DAVE) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event @@ -238,7 +262,8 @@ async fn set_role_admin_success() { .set_role_admin(MINTER_ROLE, MODERATOR_ROLE) .with_actor_id(ALICE) .await - .expect("Failed for Alice to set MODERATOR_ROLE as admin for MINTER_ROLE"); + .expect("Failed for Alice to set MODERATOR_ROLE as admin for MINTER_ROLE") + .unwrap(); let (actor, event) = events.next().await.unwrap(); assert_eq!(actor, pid); @@ -261,7 +286,8 @@ async fn set_role_admin_success() { .grant_role(MINTER_ROLE, BOB) .with_actor_id(DAVE) .await - .expect("Failed for Dave to grant MINTER_ROLE to Bob"); + .expect("Failed for Dave to grant MINTER_ROLE to Bob") + .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event // Bob should have MINTER_ROLE @@ -273,7 +299,8 @@ async fn set_role_admin_success() { .grant_role(MINTER_ROLE, CHARLIE) .with_actor_id(ALICE) .await - .expect("Alice (super admin) should still be able to grant roles"); + .expect("Alice (super admin) should still be able to grant roles") + .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event let has_role = access_control_service.has_role(MINTER_ROLE, CHARLIE).await; @@ -284,7 +311,8 @@ async fn set_role_admin_success() { .set_role_admin(MINTER_ROLE, DEFAULT_ADMIN_ROLE) .with_actor_id(DAVE) // Dave is MODERATOR_ROLE, which is admin for MINTER_ROLE .await - .expect("Failed for Dave to revert admin role"); + .expect("Failed for Dave to revert admin role") + .unwrap(); events.next().await.unwrap(); // Consume RoleAdminChanged event // Alice should now be able to grant MINTER_ROLE again @@ -292,7 +320,8 @@ async fn set_role_admin_success() { .grant_role(MINTER_ROLE, DAVE) .with_actor_id(ALICE) .await - .expect("Failed for Alice to grant MINTER_ROLE to Dave after revert"); + .expect("Failed for Alice to grant MINTER_ROLE to Dave after revert") + .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event let has_role = access_control_service.has_role(MINTER_ROLE, DAVE).await; assert_ok!(has_role, true); @@ -307,10 +336,15 @@ async fn set_role_admin_fail_unauthorized() { let res = access_control_service .set_role_admin(MINTER_ROLE, MODERATOR_ROLE) .with_actor_id(CHARLIE) - .await; - assert_str_panic( - res.unwrap_err(), - "Access denied: account 0x0000000000000000000000002c00000000000000000000000000000000000000 does not have role [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + .await + .unwrap(); + let err = res.unwrap_err(); + assert_eq!( + err.0, + format!( + "Access denied: account {:?} does not have role {:?}", + CHARLIE, DEFAULT_ADMIN_ROLE + ) ); // Admin for MINTER_ROLE should still be DEFAULT_ADMIN_ROLE @@ -330,6 +364,7 @@ async fn multiple_roles() { .grant_role(MINTER_ROLE, BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event @@ -338,6 +373,7 @@ async fn multiple_roles() { .grant_role(PAUSER_ROLE, BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); events.next().await.unwrap(); // Consume RoleGranted event @@ -359,7 +395,8 @@ async fn self_admin_role_success() { .set_role_admin(MINTER_ROLE, MINTER_ROLE) .with_actor_id(ALICE) .await - .expect("Failed to set self-admin role"); + .expect("Failed to set self-admin role") + .unwrap(); // 1. Alice (Super Admin) should still be able to grant MINTER_ROLE to Bob // even though she doesn't have MINTER_ROLE herself. @@ -367,7 +404,8 @@ async fn self_admin_role_success() { .grant_role(MINTER_ROLE, BOB) .with_actor_id(ALICE) .await - .expect("Super Admin should be able to grant self-administered role"); + .expect("Super Admin should be able to grant self-administered role") + .unwrap(); assert_ok!( access_control_service.has_role(MINTER_ROLE, BOB).await, @@ -380,7 +418,8 @@ async fn self_admin_role_success() { .grant_role(MINTER_ROLE, CHARLIE) .with_actor_id(BOB) .await - .expect("Member of self-administered role should be able to grant it to others"); + .expect("Member of self-administered role should be able to grant it to others") + .unwrap(); assert_ok!( access_control_service.has_role(MINTER_ROLE, CHARLIE).await, @@ -401,7 +440,8 @@ async fn batch_grant_success() { .grant_roles_batch(roles.clone(), BOB) .with_actor_id(ALICE) .await - .expect("Batch grant failed"); + .expect("Batch grant failed") + .unwrap(); for role_id in roles.clone() { let (actor, event) = events.next().await.unwrap(); @@ -431,13 +471,15 @@ async fn batch_revoke_success() { .grant_roles_batch(roles.clone(), BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); access_control_service .revoke_roles_batch(roles.clone(), BOB) .with_actor_id(ALICE) .await - .expect("Batch revoke failed"); + .expect("Batch revoke failed") + .unwrap(); for role in roles { assert_ok!(access_control_service.has_role(role, BOB).await, false); @@ -454,6 +496,7 @@ async fn enumeration_roles_success() { .grant_roles_batch(roles.clone(), BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); // count: admin + 3 new = 4 @@ -478,6 +521,7 @@ async fn enumeration_members_success() { .grant_role(MINTER_ROLE, m) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); } @@ -508,6 +552,7 @@ async fn enumeration_member_roles_success() { .grant_roles_batch(roles.clone(), BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); assert_ok!(access_control_service.get_member_role_count(BOB).await, 3); @@ -532,11 +577,13 @@ async fn batch_grant_atomic_failure() { .grant_role(MODERATOR_ROLE, BOB) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); access_control_service .set_role_admin(MINTER_ROLE, MODERATOR_ROLE) .with_actor_id(ALICE) .await + .unwrap() .unwrap(); // 2. Bob tries to grant [MINTER_ROLE, PAUSER_ROLE] to Charlie. @@ -545,7 +592,8 @@ async fn batch_grant_atomic_failure() { let res = access_control_service .grant_roles_batch(roles, CHARLIE) .with_actor_id(BOB) - .await; + .await + .unwrap(); // 3. Must fail assert!(res.is_err()); diff --git a/tests/access-control-test/client/access_control_test_client.idl b/tests/access-control-test/client/access_control_test_client.idl index 874fbae..82ec8f1 100644 --- a/tests/access-control-test/client/access_control_test_client.idl +++ b/tests/access-control-test/client/access_control_test_client.idl @@ -1,148 +1,169 @@ -/// Pagination parameters for listing roles or members. -type Pagination = struct { - /// The number of items to skip. - offset: u32, - /// The maximum number of items to return. - limit: u32, -}; -constructor { - New : (); -}; +!@sails: 1.0.0-beta.2 -service AccessControl { - /// Grants `role_id` to `target_account`. - /// - /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` - /// event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - GrantRole : (role_id: [u8, 32], target_account: actor_id) -> null; - /// Grants `role_ids` to `target_account`. - /// - /// If `target_account` had not been already granted any of the `role_ids`, - /// emits a `RoleGranted` event for each newly granted role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - GrantRolesBatch : (role_ids: vec [u8, 32], target_account: actor_id) -> null; - /// Revokes `role_id` from the calling account. - /// - /// Roles are often managed via `grant_role` and `revoke_role`: this function's - /// purpose is to provide a mechanism for accounts to lose their privileges - /// if they are compromised (such as when a trusted device is misplaced). - /// - /// If the calling account had been granted `role_id`, emits a `RoleRevoked` - /// event. - /// - /// # Requirements - /// - /// * The caller must be `account_id`. - RenounceRole : (role_id: [u8, 32], account_id: actor_id) -> null; - /// Revokes `role_id` from `target_account`. - /// - /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - RevokeRole : (role_id: [u8, 32], target_account: actor_id) -> null; - /// Revokes `role_ids` from `target_account`. - /// - /// If `target_account` had been granted any of the `role_ids`, - /// emits a `RoleRevoked` event for each newly revoked role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - RevokeRolesBatch : (role_ids: vec [u8, 32], target_account: actor_id) -> null; - /// Sets `new_admin_role_id` as the admin role for `role_id`. - /// - /// Emits a `RoleAdminChanged` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - SetRoleAdmin : (role_id: [u8, 32], new_admin_role_id: [u8, 32]) -> null; - /// Returns the number of roles assigned to the specified member. - /// - /// # Arguments - /// - /// * `member_id` - The account identifier. - query GetMemberRoleCount : (member_id: actor_id) -> u32; - /// Returns a list of roles assigned to the specified member with pagination. - /// - /// # Arguments - /// - /// * `member_id` - The account identifier. - /// * `query` - Optional pagination configuration. - query GetMemberRoles : (member_id: actor_id, query: opt Pagination) -> vec [u8, 32]; - /// Returns the admin role ID that controls `role_id`. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - /// - /// # Returns - /// - /// The `RoleId` of the administrator. - query GetRoleAdmin : (role_id: [u8, 32]) -> [u8, 32]; - /// Returns the total number of roles in the system. - query GetRoleCount : () -> u32; - /// Returns the number of members in the specified role. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - query GetRoleMemberCount : (role_id: [u8, 32]) -> u32; - /// Returns a list of members in the specified role with pagination. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - /// * `query` - Optional pagination configuration. - query GetRoleMembers : (role_id: [u8, 32], query: opt Pagination) -> vec actor_id; - /// Returns a list of role IDs with pagination. - /// - /// # Arguments - /// - /// * `query` - Optional pagination configuration. - query GetRoles : (query: opt Pagination) -> vec [u8, 32]; - /// Checks if `account_id` has been granted `role_id`. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - /// * `account_id` - The account identifier. - /// - /// # Returns - /// - /// `true` if the account possesses the role. - query HasRole : (role_id: [u8, 32], account_id: actor_id) -> bool; - - events { - /// Emitted when `target_account` is granted `role_id`. - RoleGranted: struct { - role_id: [u8, 32], - target_account: actor_id, - sender: actor_id, - }; - /// Emitted when `role_id` is revoked from `target_account`. - RoleRevoked: struct { - role_id: [u8, 32], - target_account: actor_id, - sender: actor_id, - }; - /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. - RoleAdminChanged: struct { - role_id: [u8, 32], - previous_admin_role_id: [u8, 32], - new_admin_role_id: [u8, 32], - sender: actor_id, - }; - } -}; +service AccessControl@0x26662bab76fe8d0f { + events { + /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. + RoleAdminChanged { + role_id: [u8; 32], + previous_admin_role_id: [u8; 32], + new_admin_role_id: [u8; 32], + sender: ActorId, + }, + /// Emitted when `target_account` is granted `role_id`. + RoleGranted { + role_id: [u8; 32], + target_account: ActorId, + sender: ActorId, + }, + /// Emitted when `role_id` is revoked from `target_account`. + RoleRevoked { + role_id: [u8; 32], + target_account: ActorId, + sender: ActorId, + }, + } + functions { + /// Returns the number of roles assigned to the specified member. + /// + /// # Arguments + /// + /// * `member_id` - The account identifier. + @query + GetMemberRoleCount(member_id: ActorId) -> u32; + /// Returns a list of roles assigned to the specified member with pagination. + /// + /// # Arguments + /// + /// * `member_id` - The account identifier. + /// * `query` - Optional pagination configuration. + @query + GetMemberRoles(member_id: ActorId, query: Option) -> [[u8; 32]]; + /// Returns the admin role ID that controls `role_id`. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + /// + /// # Returns + /// + /// The `RoleId` of the administrator. + @query + GetRoleAdmin(role_id: [u8; 32]) -> [u8; 32]; + /// Returns the total number of roles in the system. + @query + GetRoleCount() -> u32; + /// Returns the number of members in the specified role. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + @query + GetRoleMemberCount(role_id: [u8; 32]) -> u32; + /// Returns a list of members in the specified role with pagination. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + /// * `query` - Optional pagination configuration. + @query + GetRoleMembers(role_id: [u8; 32], query: Option) -> [ActorId]; + /// Returns a list of role IDs with pagination. + /// + /// # Arguments + /// + /// * `query` - Optional pagination configuration. + @query + GetRoles(query: Option) -> [[u8; 32]]; + /// Grants `role_id` to `target_account`. + /// + /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` + /// event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + GrantRole(role_id: [u8; 32], target_account: ActorId) throws Error; + /// Grants `role_ids` to `target_account`. + /// + /// If `target_account` had not been already granted any of the `role_ids`, + /// emits a `RoleGranted` event for each newly granted role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. + GrantRolesBatch(role_ids: [[u8; 32]], target_account: ActorId) throws Error; + /// Checks if `account_id` has been granted `role_id`. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + /// * `account_id` - The account identifier. + /// + /// # Returns + /// + /// `true` if the account possesses the role. + @query + HasRole(role_id: [u8; 32], account_id: ActorId) -> bool; + /// Revokes `role_id` from the calling account. + /// + /// Roles are often managed via `grant_role` and `revoke_role`: this function's + /// purpose is to provide a mechanism for accounts to lose their privileges + /// if they are compromised (such as when a trusted device is misplaced). + /// + /// If the calling account had been granted `role_id`, emits a `RoleRevoked` + /// event. + /// + /// # Requirements + /// + /// * The caller must be `account_id`. + RenounceRole(role_id: [u8; 32], account_id: ActorId) throws Error; + /// Revokes `role_id` from `target_account`. + /// + /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + RevokeRole(role_id: [u8; 32], target_account: ActorId) throws Error; + /// Revokes `role_ids` from `target_account`. + /// + /// If `target_account` had been granted any of the `role_ids`, + /// emits a `RoleRevoked` event for each newly revoked role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. + RevokeRolesBatch(role_ids: [[u8; 32]], target_account: ActorId) throws Error; + /// Sets `new_admin_role_id` as the admin role for `role_id`. + /// + /// Emits a `RoleAdminChanged` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + SetRoleAdmin(role_id: [u8; 32], new_admin_role_id: [u8; 32]) throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + /// Pagination parameters for listing roles or members. + struct Pagination { + /// The number of items to skip. + offset: u32, + /// The maximum number of items to return. + limit: u32, + } + } +} +program AccessControlTestClient { + constructors { + New(); + } + services { + AccessControl@0x26662bab76fe8d0f, + } +} diff --git a/tests/access-control-test/client/src/access_control_test_client.rs b/tests/access-control-test/client/src/access_control_test_client.rs index ee8336f..94b7e30 100644 --- a/tests/access-control-test/client/src/access_control_test_client.rs +++ b/tests/access-control-test/client/src/access_control_test_client.rs @@ -2,13 +2,20 @@ #[allow(unused_imports)] use sails_rs::{client::*, collections::*, prelude::*}; pub struct AccessControlTestClientProgram; + +impl AccessControlTestClientProgram { + pub const ROUTE_ID_ACCESS_CONTROL: u8 = 1; +} + impl sails_rs::client::Program for AccessControlTestClientProgram {} + pub trait AccessControlTestClient { type Env: sails_rs::client::GearEnv; fn access_control( &self, ) -> sails_rs::client::Service; } + impl AccessControlTestClient for sails_rs::client::Actor { @@ -16,7 +23,7 @@ impl AccessControlTestClient fn access_control( &self, ) -> sails_rs::client::Service { - self.service(stringify!(AccessControl)) + self.service(AccessControlTestClientProgram::ROUTE_ID_ACCESS_CONTROL) } } pub trait AccessControlTestClientCtors { @@ -27,6 +34,7 @@ pub trait AccessControlTestClientCtors { self, ) -> sails_rs::client::PendingCtor; } + impl AccessControlTestClientCtors for sails_rs::client::Deployment { @@ -40,93 +48,34 @@ impl AccessControlTestClientCtors pub mod io { use super::*; - sails_rs::io_struct_impl!(New () -> ()); + sails_rs::io_struct_impl!(New () -> (), 0); } pub mod access_control { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + /// Pagination parameters for listing roles or members. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Pagination { + /// The number of items to skip. + pub offset: u32, + /// The maximum number of items to return. + pub limit: u32, + } + pub trait AccessControl { type Env: sails_rs::client::GearEnv; - /// Grants `role_id` to `target_account`. - /// - /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` - /// event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - fn grant_role( - &mut self, - role_id: [u8; 32], - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Grants `role_ids` to `target_account`. - /// - /// If `target_account` had not been already granted any of the `role_ids`, - /// emits a `RoleGranted` event for each newly granted role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - fn grant_roles_batch( - &mut self, - role_ids: Vec<[u8; 32]>, - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Revokes `role_id` from the calling account. - /// - /// Roles are often managed via `grant_role` and `revoke_role`: this function's - /// purpose is to provide a mechanism for accounts to lose their privileges - /// if they are compromised (such as when a trusted device is misplaced). - /// - /// If the calling account had been granted `role_id`, emits a `RoleRevoked` - /// event. - /// - /// # Requirements - /// - /// * The caller must be `account_id`. - fn renounce_role( - &mut self, - role_id: [u8; 32], - account_id: ActorId, - ) -> sails_rs::client::PendingCall; - /// Revokes `role_id` from `target_account`. - /// - /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - fn revoke_role( - &mut self, - role_id: [u8; 32], - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Revokes `role_ids` from `target_account`. - /// - /// If `target_account` had been granted any of the `role_ids`, - /// emits a `RoleRevoked` event for each newly revoked role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - fn revoke_roles_batch( - &mut self, - role_ids: Vec<[u8; 32]>, - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Sets `new_admin_role_id` as the admin role for `role_id`. - /// - /// Emits a `RoleAdminChanged` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - fn set_role_admin( - &mut self, - role_id: [u8; 32], - new_admin_role_id: [u8; 32], - ) -> sails_rs::client::PendingCall; /// Returns the number of roles assigned to the specified member. /// /// # Arguments @@ -191,6 +140,32 @@ pub mod access_control { &self, query: Option, ) -> sails_rs::client::PendingCall; + /// Grants `role_id` to `target_account`. + /// + /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` + /// event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + fn grant_role( + &mut self, + role_id: [u8; 32], + target_account: ActorId, + ) -> sails_rs::client::PendingCall; + /// Grants `role_ids` to `target_account`. + /// + /// If `target_account` had not been already granted any of the `role_ids`, + /// emits a `RoleGranted` event for each newly granted role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. + fn grant_roles_batch( + &mut self, + role_ids: Vec<[u8; 32]>, + target_account: ActorId, + ) -> sails_rs::client::PendingCall; /// Checks if `account_id` has been granted `role_id`. /// /// # Arguments @@ -206,54 +181,73 @@ pub mod access_control { role_id: [u8; 32], account_id: ActorId, ) -> sails_rs::client::PendingCall; - } - pub struct AccessControlImpl; - impl AccessControl - for sails_rs::client::Service - { - type Env = E; - fn grant_role( - &mut self, - role_id: [u8; 32], - target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, target_account)) - } - fn grant_roles_batch( - &mut self, - role_ids: Vec<[u8; 32]>, - target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_ids, target_account)) - } + /// Revokes `role_id` from the calling account. + /// + /// Roles are often managed via `grant_role` and `revoke_role`: this function's + /// purpose is to provide a mechanism for accounts to lose their privileges + /// if they are compromised (such as when a trusted device is misplaced). + /// + /// If the calling account had been granted `role_id`, emits a `RoleRevoked` + /// event. + /// + /// # Requirements + /// + /// * The caller must be `account_id`. fn renounce_role( &mut self, role_id: [u8; 32], account_id: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, account_id)) - } + ) -> sails_rs::client::PendingCall; + /// Revokes `role_id` from `target_account`. + /// + /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. fn revoke_role( &mut self, role_id: [u8; 32], target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, target_account)) - } + ) -> sails_rs::client::PendingCall; + /// Revokes `role_ids` from `target_account`. + /// + /// If `target_account` had been granted any of the `role_ids`, + /// emits a `RoleRevoked` event for each newly revoked role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. fn revoke_roles_batch( &mut self, role_ids: Vec<[u8; 32]>, target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_ids, target_account)) - } + ) -> sails_rs::client::PendingCall; + /// Sets `new_admin_role_id` as the admin role for `role_id`. + /// + /// Emits a `RoleAdminChanged` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. fn set_role_admin( &mut self, role_id: [u8; 32], new_admin_role_id: [u8; 32], - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, new_admin_role_id)) - } + ) -> sails_rs::client::PendingCall; + } + + pub struct AccessControlImpl; + + impl sails_rs::client::Identifiable for AccessControlImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([38, 102, 43, 171, 118, 254, 141, 15]); + } + + impl AccessControl + for sails_rs::client::Service + { + type Env = E; fn get_member_role_count( &self, member_id: ActorId, @@ -295,6 +289,20 @@ pub mod access_control { ) -> sails_rs::client::PendingCall { self.pending_call((query,)) } + fn grant_role( + &mut self, + role_id: [u8; 32], + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, target_account)) + } + fn grant_roles_batch( + &mut self, + role_ids: Vec<[u8; 32]>, + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_ids, target_account)) + } fn has_role( &self, role_id: [u8; 32], @@ -302,68 +310,104 @@ pub mod access_control { ) -> sails_rs::client::PendingCall { self.pending_call((role_id, account_id)) } + fn renounce_role( + &mut self, + role_id: [u8; 32], + account_id: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, account_id)) + } + fn revoke_role( + &mut self, + role_id: [u8; 32], + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, target_account)) + } + fn revoke_roles_batch( + &mut self, + role_ids: Vec<[u8; 32]>, + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_ids, target_account)) + } + fn set_role_admin( + &mut self, + role_id: [u8; 32], + new_admin_role_id: [u8; 32], + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, new_admin_role_id)) + } } pub mod io { use super::*; - sails_rs::io_struct_impl!(GrantRole (role_id: [u8; 32], target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(GrantRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(RenounceRole (role_id: [u8; 32], account_id: ActorId) -> ()); - sails_rs::io_struct_impl!(RevokeRole (role_id: [u8; 32], target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(RevokeRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(SetRoleAdmin (role_id: [u8; 32], new_admin_role_id: [u8; 32]) -> ()); - sails_rs::io_struct_impl!(GetMemberRoleCount (member_id: ActorId) -> u32); - sails_rs::io_struct_impl!(GetMemberRoles (member_id: ActorId, query: Option) -> Vec<[u8; 32]>); - sails_rs::io_struct_impl!(GetRoleAdmin (role_id: [u8; 32]) -> [u8; 32]); - sails_rs::io_struct_impl!(GetRoleCount () -> u32); - sails_rs::io_struct_impl!(GetRoleMemberCount (role_id: [u8; 32]) -> u32); - sails_rs::io_struct_impl!(GetRoleMembers (role_id: [u8; 32], query: Option) -> Vec); - sails_rs::io_struct_impl!(GetRoles (query: Option) -> Vec<[u8; 32]>); - sails_rs::io_struct_impl!(HasRole (role_id: [u8; 32], account_id: ActorId) -> bool); + sails_rs::io_struct_impl!(GetMemberRoleCount (member_id: ActorId) -> u32, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetMemberRoles (member_id: ActorId, query: super::Option) -> Vec<[u8; 32]>, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleAdmin (role_id: [u8; 32]) -> [u8; 32], 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleCount () -> u32, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleMemberCount (role_id: [u8; 32]) -> u32, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleMembers (role_id: [u8; 32], query: super::Option) -> Vec, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoles (query: super::Option) -> Vec<[u8; 32]>, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GrantRole (role_id: [u8; 32], target_account: ActorId) -> () | super::Error, 7, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GrantRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> () | super::Error, 8, ::INTERFACE_ID); + sails_rs::io_struct_impl!(HasRole (role_id: [u8; 32], account_id: ActorId) -> bool, 9, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RenounceRole (role_id: [u8; 32], account_id: ActorId) -> () | super::Error, 10, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RevokeRole (role_id: [u8; 32], target_account: ActorId) -> () | super::Error, 11, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RevokeRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> () | super::Error, 12, ::INTERFACE_ID); + sails_rs::io_struct_impl!(SetRoleAdmin (role_id: [u8; 32], new_admin_role_id: [u8; 32]) -> () | super::Error, 13, ::INTERFACE_ID); } #[cfg(not(target_arch = "wasm32"))] pub mod events { use super::*; - #[derive(PartialEq, Debug, Encode, Decode)] + #[derive(PartialEq, Debug, Encode, Decode, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] + #[reflect_hash(crate = sails_rs)] pub enum AccessControlEvents { + /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. + #[codec(index = 0)] + RoleAdminChanged { + role_id: [u8; 32], + previous_admin_role_id: [u8; 32], + new_admin_role_id: [u8; 32], + sender: ActorId, + }, /// Emitted when `target_account` is granted `role_id`. + #[codec(index = 1)] RoleGranted { role_id: [u8; 32], target_account: ActorId, sender: ActorId, }, /// Emitted when `role_id` is revoked from `target_account`. + #[codec(index = 2)] RoleRevoked { role_id: [u8; 32], target_account: ActorId, sender: ActorId, }, - /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. - RoleAdminChanged { - role_id: [u8; 32], - previous_admin_role_id: [u8; 32], - new_admin_role_id: [u8; 32], - sender: ActorId, - }, } - impl sails_rs::client::Event for AccessControlEvents { - const EVENT_NAMES: &'static [Route] = - &["RoleGranted", "RoleRevoked", "RoleAdminChanged"]; + + impl AccessControlEvents { + pub fn entry_id(&self) -> u16 { + match self { + Self::RoleAdminChanged { .. } => 0, + Self::RoleGranted { .. } => 1, + Self::RoleRevoked { .. } => 2, + } + } + } + + impl sails_rs::client::Event for AccessControlEvents {} + + impl sails_rs::client::Identifiable for AccessControlEvents { + const INTERFACE_ID: sails_rs::InterfaceId = + ::INTERFACE_ID; } + impl sails_rs::client::ServiceWithEvents for AccessControlImpl { type Event = AccessControlEvents; } } } -/// Pagination parameters for listing roles or members. -#[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo)] -#[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] -pub struct Pagination { - /// The number of items to skip. - pub offset: u32, - /// The maximum number of items to return. - pub limit: u32, -} diff --git a/tests/awesome-sails-test/app/tests/common/mod.rs b/tests/awesome-sails-test/app/tests/common/mod.rs index 31ae4a9..b0ac2b4 100644 --- a/tests/awesome-sails-test/app/tests/common/mod.rs +++ b/tests/awesome-sails-test/app/tests/common/mod.rs @@ -91,7 +91,7 @@ pub async fn deploy_with_data( .deploy::(code_id, b"salt".to_vec()) .new() .await - .expect("failed to deploy program"); + .unwrap(); let program_id = program.id(); @@ -101,35 +101,22 @@ pub async fn deploy_with_data( .allocate_next_balances_shard() .await .expect("failed to allocate next balances shard") + .unwrap() {} while vft_extension .allocate_next_allowances_shard() .await .expect("failed to allocate next balances shard") + .unwrap() {} program .test() .set(allowances, balances, expiry_period) .await - .expect("failed to set data"); + .expect("failed to set data") + .unwrap(); (program, env, program_id) } - -#[track_caller] -pub fn assert_str_panic(e: GtestError, exp: &str) { - match e { - GtestError::ReplyHasError( - ErrorReplyReason::Execution(SimpleExecutionError::UserspacePanic), - res, - ) => { - let actual = String::from_utf8_lossy(&res); - let expected = - format!("panicked with 'called `Result::unwrap()` on an `Err` value: {exp}'"); - assert_eq!(actual, expected); - } - _ => core::panic!("not an expected error reply type: {e:?}"), - } -} diff --git a/tests/awesome-sails-test/app/tests/gtest.rs b/tests/awesome-sails-test/app/tests/gtest.rs index ae829a4..13bf43b 100644 --- a/tests/awesome-sails-test/app/tests/gtest.rs +++ b/tests/awesome-sails-test/app/tests/gtest.rs @@ -28,7 +28,7 @@ use awesome_sails_test_client::{ vft_extension::VftExtension, }; use awesome_sails_utils::{assert_ok, math::Max}; -use common::{ALICE, BOB, CHARLIE, DAVE, assert_str_panic, deploy_with_data}; +use common::{ALICE, BOB, CHARLIE, DAVE, deploy_with_data}; use futures::StreamExt; use sails_rs::{U256, prelude::*}; @@ -49,20 +49,20 @@ async fn allowance() { // Approve is returned if exists. { let res = vft_extension_service.allowance_of(ALICE, BOB).await; - assert_ok!(res, Some((U256::exp10(MAGIC), BN))); + assert_ok!(res.unwrap(), Some((U256::exp10(MAGIC), BN))); let res = vft_service.allowance(ALICE, BOB).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); } // # Test case #2. // U256::zero() is returned if not exists. { let res = vft_extension_service.allowance_of(BOB, ALICE).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); let res = vft_service.allowance(BOB, ALICE).await; - assert_ok!(res, U256::zero()); + assert_ok!(res.unwrap(), U256::zero()); } } @@ -79,7 +79,7 @@ async fn approve() { // Allowance from Alice to Bob doesn't exist and created. { let res = vft_service.approve(BOB, U256::exp10(MAGIC)).await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -93,7 +93,7 @@ async fn approve() { ); let res = vft_service.allowance(ALICE, BOB).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); } // # Test case #2. @@ -103,11 +103,12 @@ async fn approve() { .allowance_of(ALICE, BOB) .await .expect("infallible") - .expect("infallible"); + .expect("infallible") + .unwrap(); let res = vft_service.approve(BOB, U256::exp10(MAGIC - 1)).await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -124,7 +125,8 @@ async fn approve() { .allowance_of(ALICE, BOB) .await .expect("infallible") - .expect("infallible"); + .expect("infallible") + .unwrap(); assert_eq!(res, U256::exp10(MAGIC - 1)); @@ -136,14 +138,14 @@ async fn approve() { { let res = vft_service.approve(BOB, U256::exp10(MAGIC - 1)).await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); } // # Test case #4. // Allowance from Alice to Bob exists and removed. { let res = vft_service.approve(BOB, U256::zero()).await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -157,36 +159,36 @@ async fn approve() { ); let res = vft_service.allowance(ALICE, BOB).await; - assert_ok!(res, U256::zero()); + assert_ok!(res.unwrap(), U256::zero()); let res = vft_extension_service.allowance_of(ALICE, BOB).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); } // # Test case #5. // Allowance from Alice to Bob doesn't exists and not created. { let res = vft_service.approve(BOB, U256::zero()).await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_service.allowance(ALICE, BOB).await; - assert_ok!(res, U256::zero()); + assert_ok!(res.unwrap(), U256::zero()); } // # Test case #6. // Allowance is always noop on owner == spender. { let res = vft_service.approve(ALICE, U256::exp10(MAGIC)).await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_service.allowance(ALICE, ALICE).await; - assert_ok!(res, U256::zero()); + assert_ok!(res.unwrap(), U256::zero()); let res = vft_service.approve(ALICE, U256::zero()).await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_service.allowance(ALICE, ALICE).await; - assert_ok!(res, U256::zero()); + assert_ok!(res.unwrap(), U256::zero()); } } @@ -204,20 +206,20 @@ async fn balance_of() { // Balance is returned if exists. { let res = vft_extension_service.balance_of(ALICE).await; - assert_ok!(res, Some(U256::exp10(MAGIC))); + assert_ok!(res.unwrap(), Some(U256::exp10(MAGIC))); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); } // # Test case #2. // U256::zero() is returned if not exists. { let res = vft_extension_service.balance_of(BOB).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); let res = vft_service.balance_of(BOB).await; - assert_ok!(res, U256::zero()); + assert_ok!(res.unwrap(), U256::zero()); } } @@ -242,7 +244,8 @@ async fn transfer() { .with_actor_id(ALICE) .await; - assert_str_panic(res.unwrap_err(), "insufficient balance"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("insufficient balance")); } // # Test case #2. @@ -253,7 +256,8 @@ async fn transfer() { .with_actor_id(BOB) .await; - assert_str_panic(res.unwrap_err(), "insufficient balance"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("insufficient balance")); } // # Test case #3. @@ -268,7 +272,8 @@ async fn transfer() { .with_actor_id(DAVE) .await; - assert_str_panic(res.unwrap_err(), "balance or supply overflow"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("balance or supply overflow")); } // # Test case #4. @@ -279,7 +284,7 @@ async fn transfer() { .with_actor_id(BOB) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -293,10 +298,10 @@ async fn transfer() { ); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC - 1)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC - 1)); let res = vft_service.balance_of(BOB).await; - assert_ok!(res, U256::exp10(MAGIC) - U256::exp10(MAGIC - 1)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC) - U256::exp10(MAGIC - 1)); } // # Test case #5. @@ -307,7 +312,7 @@ async fn transfer() { .with_actor_id(BOB) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -321,11 +326,11 @@ async fn transfer() { ); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC - 1) + U256::exp10(MAGIC - 2)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC - 1) + U256::exp10(MAGIC - 2)); let res = vft_service.balance_of(BOB).await; assert_ok!( - res, + res.unwrap(), U256::exp10(MAGIC) - U256::exp10(MAGIC - 1) - U256::exp10(MAGIC - 2) ); } @@ -341,7 +346,7 @@ async fn transfer() { .with_actor_id(BOB) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -355,10 +360,10 @@ async fn transfer() { ); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); let res = vft_extension_service.balance_of(BOB).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); } // # Test case #7. @@ -369,7 +374,7 @@ async fn transfer() { .with_actor_id(ALICE) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -383,10 +388,10 @@ async fn transfer() { ); let res = vft_service.balance_of(CHARLIE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); let res = vft_extension_service.balance_of(ALICE).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); } // # Test case #8. @@ -397,20 +402,20 @@ async fn transfer() { .with_actor_id(ALICE) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_extension_service.balance_of(ALICE).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); let res = vft_service .transfer(CHARLIE, U256::exp10(MAGIC)) .with_actor_id(CHARLIE) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_service.balance_of(CHARLIE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); } // # Test case #9. @@ -421,20 +426,20 @@ async fn transfer() { .with_actor_id(ALICE) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_service.balance_of(CHARLIE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); let res = vft_service .transfer(ALICE, U256::zero()) .with_actor_id(CHARLIE) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_extension_service.balance_of(ALICE).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); } } @@ -462,7 +467,7 @@ async fn transfer_from() { .with_actor_id(BOB) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); } // # Test case #2. @@ -473,7 +478,7 @@ async fn transfer_from() { .with_actor_id(BOB) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -487,10 +492,10 @@ async fn transfer_from() { ); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); let res = vft_extension_service.balance_of(BOB).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); } // # Test case #3. @@ -501,20 +506,20 @@ async fn transfer_from() { .with_actor_id(BOB) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_extension_service.balance_of(BOB).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); let res = vft_service .transfer_from(ALICE, ALICE, U256::exp10(MAGIC)) .with_actor_id(ALICE) .await; - assert_ok!(res, false); + assert_ok!(res.unwrap(), false); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); } // # Test case #4. @@ -525,7 +530,8 @@ async fn transfer_from() { .with_actor_id(BOB) .await; - assert_str_panic(res.unwrap_err(), "insufficient allowance"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("insufficient allowance")); } // # Test case #5. @@ -536,7 +542,7 @@ async fn transfer_from() { .with_actor_id(ALICE) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -554,7 +560,8 @@ async fn transfer_from() { .with_actor_id(BOB) .await; - assert_str_panic(res.unwrap_err(), "insufficient allowance"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("insufficient allowance")); } // # Test case #6. @@ -565,7 +572,7 @@ async fn transfer_from() { .with_actor_id(ALICE) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -583,7 +590,8 @@ async fn transfer_from() { .with_actor_id(BOB) .await; - assert_str_panic(res.unwrap_err(), "insufficient balance"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("insufficient balance")); } // # Test case #7. @@ -593,14 +601,15 @@ async fn transfer_from() { .allowance_of(ALICE, BOB) .await .expect("infallible") - .expect("infallible"); + .expect("infallible") + .unwrap(); let res = vft_service .transfer_from(ALICE, CHARLIE, U256::exp10(MAGIC - 1)) .with_actor_id(BOB) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -614,16 +623,17 @@ async fn transfer_from() { ); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res, U256::exp10(MAGIC) - U256::exp10(MAGIC - 1)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC) - U256::exp10(MAGIC - 1)); let res = vft_service.balance_of(CHARLIE).await; - assert_ok!(res, U256::exp10(MAGIC - 1)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC - 1)); let (res, bn2) = vft_extension_service .allowance_of(ALICE, BOB) .await .expect("infallible") - .expect("infallible"); + .expect("infallible") + .unwrap(); assert_eq!(res, U256::MAX); @@ -638,7 +648,7 @@ async fn transfer_from() { .with_actor_id(CHARLIE) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -655,14 +665,15 @@ async fn transfer_from() { .allowance_of(CHARLIE, ALICE) .await .expect("infallible") - .expect("infallible"); + .expect("infallible") + .unwrap(); let res = vft_service .transfer_from(CHARLIE, DAVE, U256::exp10(MAGIC - 3)) .with_actor_id(ALICE) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -679,7 +690,8 @@ async fn transfer_from() { .allowance_of(CHARLIE, ALICE) .await .expect("infallible") - .expect("infallible"); + .expect("infallible") + .unwrap(); assert_eq!(res, U256::exp10(MAGIC - 2) - U256::exp10(MAGIC - 3)); @@ -698,7 +710,7 @@ async fn transfer_from() { .with_actor_id(ALICE) .await; - assert_ok!(res, true); + assert_ok!(res.unwrap(), true); let (actor, event) = vft_events.next().await.unwrap(); assert_eq!(actor, pid); @@ -712,13 +724,13 @@ async fn transfer_from() { ); let res = vft_extension_service.allowance_of(CHARLIE, ALICE).await; - assert_ok!(res, None); + assert_ok!(res.unwrap(), None); let res = vft_service.balance_of(CHARLIE).await; - assert_ok!(res, U256::exp10(MAGIC - 1) - U256::exp10(MAGIC - 2)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC - 1) - U256::exp10(MAGIC - 2)); let res = vft_service.balance_of(DAVE).await; - assert_ok!(res, U256::exp10(MAGIC) + U256::exp10(MAGIC - 2)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC) + U256::exp10(MAGIC - 2)); } } @@ -739,15 +751,16 @@ async fn pause() { .grant_role(PAUSER_ROLE, ALICE) .with_actor_id(ALICE) .await - .expect("Alice failed to grant PAUSER_ROLE to herself"); + .expect("Alice failed to grant PAUSER_ROLE to herself") + .unwrap(); // Call not paused. { let res = vft_extension_service.allowance_of(ALICE, BOB).await; - assert_ok!(res, Some((U256::exp10(MAGIC), BN))); + assert_ok!(res.unwrap(), Some((U256::exp10(MAGIC), BN))); let res = vft_service.allowance(ALICE, BOB).await; - assert_ok!(res, U256::exp10(MAGIC)); + assert_ok!(res.unwrap(), U256::exp10(MAGIC)); } // Pause (Alice has PAUSER_ROLE) @@ -756,6 +769,7 @@ async fn pause() { .pause() .with_actor_id(ALICE) .await + .unwrap() .unwrap(); // Call from Alice let paused = vft_admin_service.is_paused().await.unwrap(); @@ -766,6 +780,7 @@ async fn pause() { { let res = vft_service.transfer(BOB, U256::exp10(10)).await; - assert_str_panic(res.unwrap_err(), "storage is paused"); + let err = res.unwrap().unwrap_err(); + assert!(err.0.contains("storage is paused")); } } diff --git a/tests/awesome-sails-test/client/awesome_sails_test_client.idl b/tests/awesome-sails-test/client/awesome_sails_test_client.idl index 2cdc0b7..3256f28 100644 --- a/tests/awesome-sails-test/client/awesome_sails_test_client.idl +++ b/tests/awesome-sails-test/client/awesome_sails_test_client.idl @@ -1,504 +1,592 @@ -/// Pagination parameters for listing roles or members. -type Pagination = struct { - /// The number of items to skip. - offset: u32, - /// The maximum number of items to return. - limit: u32, -}; -constructor { - New : (); -}; +!@sails: 1.0.0-beta.2 -service Test { - Set : (new_allowances: vec struct { actor_id, actor_id, u256, u32 }, new_balances: vec struct { actor_id, u256 }, expiry_period: u32) -> null; -}; +service Test@0x279e57efe3de371d { + functions { + Set(new_allowances: [(ActorId, ActorId, U256, u32)], new_balances: [(ActorId, U256)], expiry_period: u32) throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + } +} -service AccessControl { - /// Grants `role_id` to `target_account`. - /// - /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` - /// event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - GrantRole : (role_id: [u8, 32], target_account: actor_id) -> null; - /// Grants `role_ids` to `target_account`. - /// - /// If `target_account` had not been already granted any of the `role_ids`, - /// emits a `RoleGranted` event for each newly granted role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - GrantRolesBatch : (role_ids: vec [u8, 32], target_account: actor_id) -> null; - /// Revokes `role_id` from the calling account. - /// - /// Roles are often managed via `grant_role` and `revoke_role`: this function's - /// purpose is to provide a mechanism for accounts to lose their privileges - /// if they are compromised (such as when a trusted device is misplaced). - /// - /// If the calling account had been granted `role_id`, emits a `RoleRevoked` - /// event. - /// - /// # Requirements - /// - /// * The caller must be `account_id`. - RenounceRole : (role_id: [u8, 32], account_id: actor_id) -> null; - /// Revokes `role_id` from `target_account`. - /// - /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - RevokeRole : (role_id: [u8, 32], target_account: actor_id) -> null; - /// Revokes `role_ids` from `target_account`. - /// - /// If `target_account` had been granted any of the `role_ids`, - /// emits a `RoleRevoked` event for each newly revoked role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - RevokeRolesBatch : (role_ids: vec [u8, 32], target_account: actor_id) -> null; - /// Sets `new_admin_role_id` as the admin role for `role_id`. - /// - /// Emits a `RoleAdminChanged` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - SetRoleAdmin : (role_id: [u8, 32], new_admin_role_id: [u8, 32]) -> null; - /// Returns the number of roles assigned to the specified member. - /// - /// # Arguments - /// - /// * `member_id` - The account identifier. - query GetMemberRoleCount : (member_id: actor_id) -> u32; - /// Returns a list of roles assigned to the specified member with pagination. - /// - /// # Arguments - /// - /// * `member_id` - The account identifier. - /// * `query` - Optional pagination configuration. - query GetMemberRoles : (member_id: actor_id, query: opt Pagination) -> vec [u8, 32]; - /// Returns the admin role ID that controls `role_id`. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - /// - /// # Returns - /// - /// The `RoleId` of the administrator. - query GetRoleAdmin : (role_id: [u8, 32]) -> [u8, 32]; - /// Returns the total number of roles in the system. - query GetRoleCount : () -> u32; - /// Returns the number of members in the specified role. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - query GetRoleMemberCount : (role_id: [u8, 32]) -> u32; - /// Returns a list of members in the specified role with pagination. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - /// * `query` - Optional pagination configuration. - query GetRoleMembers : (role_id: [u8, 32], query: opt Pagination) -> vec actor_id; - /// Returns a list of role IDs with pagination. - /// - /// # Arguments - /// - /// * `query` - Optional pagination configuration. - query GetRoles : (query: opt Pagination) -> vec [u8, 32]; - /// Checks if `account_id` has been granted `role_id`. - /// - /// # Arguments - /// - /// * `role_id` - The role identifier. - /// * `account_id` - The account identifier. - /// - /// # Returns - /// - /// `true` if the account possesses the role. - query HasRole : (role_id: [u8, 32], account_id: actor_id) -> bool; +service AccessControl@0x26662bab76fe8d0f { + events { + /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. + RoleAdminChanged { + role_id: [u8; 32], + previous_admin_role_id: [u8; 32], + new_admin_role_id: [u8; 32], + sender: ActorId, + }, + /// Emitted when `target_account` is granted `role_id`. + RoleGranted { + role_id: [u8; 32], + target_account: ActorId, + sender: ActorId, + }, + /// Emitted when `role_id` is revoked from `target_account`. + RoleRevoked { + role_id: [u8; 32], + target_account: ActorId, + sender: ActorId, + }, + } + functions { + /// Returns the number of roles assigned to the specified member. + /// + /// # Arguments + /// + /// * `member_id` - The account identifier. + @query + GetMemberRoleCount(member_id: ActorId) -> u32; + /// Returns a list of roles assigned to the specified member with pagination. + /// + /// # Arguments + /// + /// * `member_id` - The account identifier. + /// * `query` - Optional pagination configuration. + @query + GetMemberRoles(member_id: ActorId, query: Option) -> [[u8; 32]]; + /// Returns the admin role ID that controls `role_id`. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + /// + /// # Returns + /// + /// The `RoleId` of the administrator. + @query + GetRoleAdmin(role_id: [u8; 32]) -> [u8; 32]; + /// Returns the total number of roles in the system. + @query + GetRoleCount() -> u32; + /// Returns the number of members in the specified role. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + @query + GetRoleMemberCount(role_id: [u8; 32]) -> u32; + /// Returns a list of members in the specified role with pagination. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + /// * `query` - Optional pagination configuration. + @query + GetRoleMembers(role_id: [u8; 32], query: Option) -> [ActorId]; + /// Returns a list of role IDs with pagination. + /// + /// # Arguments + /// + /// * `query` - Optional pagination configuration. + @query + GetRoles(query: Option) -> [[u8; 32]]; + /// Grants `role_id` to `target_account`. + /// + /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` + /// event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + GrantRole(role_id: [u8; 32], target_account: ActorId) throws Error; + /// Grants `role_ids` to `target_account`. + /// + /// If `target_account` had not been already granted any of the `role_ids`, + /// emits a `RoleGranted` event for each newly granted role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. + GrantRolesBatch(role_ids: [[u8; 32]], target_account: ActorId) throws Error; + /// Checks if `account_id` has been granted `role_id`. + /// + /// # Arguments + /// + /// * `role_id` - The role identifier. + /// * `account_id` - The account identifier. + /// + /// # Returns + /// + /// `true` if the account possesses the role. + @query + HasRole(role_id: [u8; 32], account_id: ActorId) -> bool; + /// Revokes `role_id` from the calling account. + /// + /// Roles are often managed via `grant_role` and `revoke_role`: this function's + /// purpose is to provide a mechanism for accounts to lose their privileges + /// if they are compromised (such as when a trusted device is misplaced). + /// + /// If the calling account had been granted `role_id`, emits a `RoleRevoked` + /// event. + /// + /// # Requirements + /// + /// * The caller must be `account_id`. + RenounceRole(role_id: [u8; 32], account_id: ActorId) throws Error; + /// Revokes `role_id` from `target_account`. + /// + /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + RevokeRole(role_id: [u8; 32], target_account: ActorId) throws Error; + /// Revokes `role_ids` from `target_account`. + /// + /// If `target_account` had been granted any of the `role_ids`, + /// emits a `RoleRevoked` event for each newly revoked role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. + RevokeRolesBatch(role_ids: [[u8; 32]], target_account: ActorId) throws Error; + /// Sets `new_admin_role_id` as the admin role for `role_id`. + /// + /// Emits a `RoleAdminChanged` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + SetRoleAdmin(role_id: [u8; 32], new_admin_role_id: [u8; 32]) throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + /// Pagination parameters for listing roles or members. + struct Pagination { + /// The number of items to skip. + offset: u32, + /// The maximum number of items to return. + limit: u32, + } + } +} - events { - /// Emitted when `target_account` is granted `role_id`. - RoleGranted: struct { - role_id: [u8, 32], - target_account: actor_id, - sender: actor_id, - }; - /// Emitted when `role_id` is revoked from `target_account`. - RoleRevoked: struct { - role_id: [u8, 32], - target_account: actor_id, - sender: actor_id, - }; - /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. - RoleAdminChanged: struct { - role_id: [u8, 32], - previous_admin_role_id: [u8, 32], - new_admin_role_id: [u8, 32], - sender: actor_id, - }; - } -}; +service Vft@0x5036d76fe61f610b { + events { + /// Emitted when an approval is granted or updated. + Approval { + owner: ActorId, + spender: ActorId, + value: U256, + }, + /// Emitted when tokens are transferred. + Transfer { + from: ActorId, + to: ActorId, + value: U256, + }, + } + functions { + /// Returns the amount of tokens that `spender` is allowed to spend on behalf of `owner`. + /// + /// # Arguments + /// + /// * `owner` - The account that owns the tokens. + /// * `spender` - The account allowed to spend the tokens. + /// + /// # Returns + /// + /// The remaining allowance as `U256`. + @query + Allowance(owner: ActorId, spender: ActorId) -> U256 throws Error; + /// Approves `spender` to spend `value` amount of tokens on behalf of the caller. + /// + /// If `value` is `U256::MAX`, the allowance is treated as infinite. + /// Emits an `Approval` event if the allowance value changes. + /// + /// # Arguments + /// + /// * `spender` - The account to be allowed to spend tokens. + /// * `value` - The amount of tokens to approve. + /// + /// # Returns + /// + /// `true` if the approval value was changed, `false` otherwise. + Approve(spender: ActorId, value: U256) -> bool throws Error; + /// Returns the token balance of `account`. + /// + /// # Arguments + /// + /// * `account` - The account to query the balance of. + /// + /// # Returns + /// + /// The balance as `U256`. + @query + BalanceOf(account: ActorId) -> U256 throws Error; + /// Returns the total supply of tokens. + /// + /// # Returns + /// + /// The total supply as `U256`. + @query + TotalSupply() -> U256 throws Error; + /// Transfers `value` amount of tokens from the caller to `to`. + /// + /// Emits a `Transfer` event. + /// + /// # Arguments + /// + /// * `to` - The recipient of the tokens. + /// * `value` - The amount of tokens to transfer. + /// + /// # Returns + /// + /// `true` if the transfer was successful. + Transfer(to: ActorId, value: U256) -> bool throws Error; + /// Transfers `value` amount of tokens from `from` to `to` using the allowance mechanism. + /// + /// The caller (spender) must have sufficient allowance from `from` (owner). + /// Emits a `Transfer` event. + /// + /// # Arguments + /// + /// * `from` - The account to transfer tokens from. + /// * `to` - The recipient of the tokens. + /// * `value` - The amount of tokens to transfer. + /// + /// # Returns + /// + /// `true` if the transfer was successful. + TransferFrom(from: ActorId, to: ActorId, value: U256) -> bool throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + } +} -service Vft { - /// Approves `spender` to spend `value` amount of tokens on behalf of the caller. - /// - /// If `value` is `U256::MAX`, the allowance is treated as infinite. - /// Emits an `Approval` event if the allowance value changes. - /// - /// # Arguments - /// - /// * `spender` - The account to be allowed to spend tokens. - /// * `value` - The amount of tokens to approve. - /// - /// # Returns - /// - /// `true` if the approval value was changed, `false` otherwise. - Approve : (spender: actor_id, value: u256) -> bool; - /// Transfers `value` amount of tokens from the caller to `to`. - /// - /// Emits a `Transfer` event. - /// - /// # Arguments - /// - /// * `to` - The recipient of the tokens. - /// * `value` - The amount of tokens to transfer. - /// - /// # Returns - /// - /// `true` if the transfer was successful. - Transfer : (to: actor_id, value: u256) -> bool; - /// Transfers `value` amount of tokens from `from` to `to` using the allowance mechanism. - /// - /// The caller (spender) must have sufficient allowance from `from` (owner). - /// Emits a `Transfer` event. - /// - /// # Arguments - /// - /// * `from` - The account to transfer tokens from. - /// * `to` - The recipient of the tokens. - /// * `value` - The amount of tokens to transfer. - /// - /// # Returns - /// - /// `true` if the transfer was successful. - TransferFrom : (from: actor_id, to: actor_id, value: u256) -> bool; - /// Returns the amount of tokens that `spender` is allowed to spend on behalf of `owner`. - /// - /// # Arguments - /// - /// * `owner` - The account that owns the tokens. - /// * `spender` - The account allowed to spend the tokens. - /// - /// # Returns - /// - /// The remaining allowance as `U256`. - query Allowance : (owner: actor_id, spender: actor_id) -> u256; - /// Returns the token balance of `account`. - /// - /// # Arguments - /// - /// * `account` - The account to query the balance of. - /// - /// # Returns - /// - /// The balance as `U256`. - query BalanceOf : (account: actor_id) -> u256; - /// Returns the total supply of tokens. - /// - /// # Returns - /// - /// The total supply as `U256`. - query TotalSupply : () -> u256; +service VftAdmin@0x6110108dc67c8d4f { + events { + /// Emitted when a burn operation occurs. + BurnerTookPlace, + /// Emitted when the program exits. + Exited(ActorId), + /// Emitted when the allowance expiry period is changed. + ExpiryPeriodChanged(u32), + /// Emitted when a mint operation occurs. + MinterTookPlace, + /// Emitted when the contract is paused. + Paused, + /// Emitted when the contract is resumed. + Resumed, + } + functions { + /// Appends a new shard to the allowances storage map. + /// + /// # Requirements + /// * Caller must have `DEFAULT_ADMIN_ROLE`. + /// + /// # Arguments + /// * `capacity` - The capacity of the new shard. + AppendAllowancesShard(capacity: u32) throws Error; + /// Appends a new shard to the balances storage map. + /// + /// # Requirements + /// * Caller must have `DEFAULT_ADMIN_ROLE`. + /// + /// # Arguments + /// * `capacity` - The capacity of the new shard. + AppendBalancesShard(capacity: u32) throws Error; + /// Approves `spender` to spend `value` from `owner`'s account. + /// + /// This is an admin function allowing the admin to set approvals arbitrarily. + /// + /// # Requirements + /// * Caller must have `DEFAULT_ADMIN_ROLE`. + /// + /// # Arguments + /// * `owner` - The account owning the tokens. + /// * `spender` - The account to be approved. + /// * `value` - The amount to approve. + ApproveFrom(owner: ActorId, spender: ActorId, value: U256) -> bool throws Error; + /// Burns `value` tokens from `from` account. + /// + /// # Requirements + /// * Caller must have `BURNER_ROLE`. + /// + /// # Arguments + /// * `from` - The account to burn tokens from. + /// * `value` - The amount to burn. + Burn(from: ActorId, value: U256) throws Error; + /// Terminates the program and sends value to `inheritor`. + /// + /// # Requirements + /// * Caller must have `DEFAULT_ADMIN_ROLE`. + /// * Program must be paused. + Exit(inheritor: ActorId) throws Error; + /// Returns `true` if the contract is paused. + @query + IsPaused() -> bool; + /// Mints `value` tokens to `to` account. + /// + /// # Requirements + /// * Caller must have `MINTER_ROLE`. + /// + /// # Arguments + /// * `to` - The recipient of the minted tokens. + /// * `value` - The amount to mint. + Mint(to: ActorId, value: U256) throws Error; + /// Pauses the contract. + /// + /// # Requirements + /// * Caller must have `PAUSER_ROLE`. + Pause() throws Error; + /// Resumes the contract. + /// + /// # Requirements + /// * Caller must have `PAUSER_ROLE`. + Resume() throws Error; + /// Sets the expiry period for allowances. + /// + /// # Requirements + /// * Caller must have `DEFAULT_ADMIN_ROLE`. + /// + /// # Arguments + /// * `period` - The new expiry period in blocks. + SetExpiryPeriod(period: u32) throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + } +} - events { - /// Emitted when an approval is granted or updated. - Approval: struct { - owner: actor_id, - spender: actor_id, - value: u256, - }; - /// Emitted when tokens are transferred. - Transfer: struct { - from: actor_id, - to: actor_id, - value: u256, - }; - } -}; +service VftExtension@0xd25cc57e5a9cfb26 { + functions { + /// Allocates the next shard for allowances storage. + /// + /// Useful when the current shard is full. + /// + /// # Returns + /// + /// `true` if a new shard was allocated, `false` otherwise. + AllocateNextAllowancesShard() -> bool throws Error; + /// Allocates the next shard for balances storage. + /// + /// Useful when the current shard is full. + /// + /// # Returns + /// + /// `true` if a new shard was allocated, `false` otherwise. + AllocateNextBalancesShard() -> bool throws Error; + /// Returns the allowance detail (amount and expiration block) for a given owner and spender. + /// + /// # Arguments + /// + /// * `owner` - The account owning the tokens. + /// * `spender` - The account allowed to spend the tokens. + /// + /// # Returns + /// + /// An `Option` containing a tuple `(U256, u32)` representing the amount and expiration block height. + @query + AllowanceOf(owner: ActorId, spender: ActorId) -> Option<(U256, u32)> throws Error; + /// Returns a list of all allowances with pagination. + /// + /// # Arguments + /// + /// * `cursor` - The index to start from. + /// * `len` - The number of items to return. + /// + /// # Returns + /// + /// A vector of allowance details. + @query + Allowances(cursor: u32, len: u32) -> [((ActorId, ActorId), (U256, u32))] throws Error; + /// Returns the balance of an account, if it exists in storage. + /// + /// Unlike `vft::balance_of` which returns 0 for non-existent accounts, this returns `None`. + /// + /// # Arguments + /// + /// * `account` - The account to query. + /// + /// # Returns + /// + /// An `Option` containing the balance. + @query + BalanceOf(account: ActorId) -> Option throws Error; + /// Returns a list of all balances with pagination. + /// + /// # Arguments + /// + /// * `cursor` - The index to start from. + /// * `len` - The number of items to return. + /// + /// # Returns + /// + /// A vector of `(ActorId, U256)` pairs. + @query + Balances(cursor: u32, len: u32) -> [(ActorId, U256)] throws Error; + /// Returns the configured allowance expiry period. + @query + ExpiryPeriod() -> u32 throws Error; + /// Removes an expired allowance. + /// + /// If the allowance from `owner` to `spender` has expired, it is removed to free up storage. + /// + /// # Arguments + /// + /// * `owner` - The account that granted the allowance. + /// * `spender` - The account that was granted the allowance. + /// + /// # Returns + /// + /// `true` if the allowance was removed, `false` otherwise (e.g., if it didn't exist). + RemoveExpiredAllowance(owner: ActorId, spender: ActorId) -> bool throws Error; + /// Transfers the entire balance from the caller to `to`. + /// + /// # Arguments + /// + /// * `to` - The recipient of the tokens. + /// + /// # Returns + /// + /// `true` if any tokens were transferred. + TransferAll(to: ActorId) -> bool throws Error; + /// Transfers the entire balance from `from` to `to` using the allowance mechanism. + /// + /// The caller must have sufficient allowance. + /// + /// # Arguments + /// + /// * `from` - The account to transfer tokens from. + /// * `to` - The recipient of the tokens. + /// + /// # Returns + /// + /// `true` if any tokens were transferred. + TransferAllFrom(from: ActorId, to: ActorId) -> bool throws Error; + /// Returns the amount of value (tokens) that are currently "unused" or reserved. + @query + UnusedValue() -> U256 throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + } +} -service VftAdmin { - /// Appends a new shard to the allowances storage map. - /// - /// # Requirements - /// * Caller must have `DEFAULT_ADMIN_ROLE`. - /// - /// # Arguments - /// * `capacity` - The capacity of the new shard. - AppendAllowancesShard : (capacity: u32) -> null; - /// Appends a new shard to the balances storage map. - /// - /// # Requirements - /// * Caller must have `DEFAULT_ADMIN_ROLE`. - /// - /// # Arguments - /// * `capacity` - The capacity of the new shard. - AppendBalancesShard : (capacity: u32) -> null; - /// Approves `spender` to spend `value` from `owner`'s account. - /// - /// This is an admin function allowing the admin to set approvals arbitrarily. - /// - /// # Requirements - /// * Caller must have `DEFAULT_ADMIN_ROLE`. - /// - /// # Arguments - /// * `owner` - The account owning the tokens. - /// * `spender` - The account to be approved. - /// * `value` - The amount to approve. - ApproveFrom : (owner: actor_id, spender: actor_id, value: u256) -> bool; - /// Burns `value` tokens from `from` account. - /// - /// # Requirements - /// * Caller must have `BURNER_ROLE`. - /// - /// # Arguments - /// * `from` - The account to burn tokens from. - /// * `value` - The amount to burn. - Burn : (from: actor_id, value: u256) -> null; - /// Terminates the program and sends value to `inheritor`. - /// - /// # Requirements - /// * Caller must have `DEFAULT_ADMIN_ROLE`. - /// * Program must be paused. - Exit : (inheritor: actor_id) -> null; - /// Mints `value` tokens to `to` account. - /// - /// # Requirements - /// * Caller must have `MINTER_ROLE`. - /// - /// # Arguments - /// * `to` - The recipient of the minted tokens. - /// * `value` - The amount to mint. - Mint : (to: actor_id, value: u256) -> null; - /// Pauses the contract. - /// - /// # Requirements - /// * Caller must have `PAUSER_ROLE`. - Pause : () -> null; - /// Resumes the contract. - /// - /// # Requirements - /// * Caller must have `PAUSER_ROLE`. - Resume : () -> null; - /// Sets the expiry period for allowances. - /// - /// # Requirements - /// * Caller must have `DEFAULT_ADMIN_ROLE`. - /// - /// # Arguments - /// * `period` - The new expiry period in blocks. - SetExpiryPeriod : (period: u32) -> null; - /// Returns `true` if the contract is paused. - query IsPaused : () -> bool; +service VftMetadata@0x594f3407bfff54f1 { + functions { + /// Returns the number of decimals used by the VFT. + /// + /// # Returns + /// + /// The decimals as a `u8`. + @query + Decimals() -> u8; + /// Returns the name of the VFT. + /// + /// # Returns + /// + /// The name as a `String`. + @query + Name() -> String; + /// Returns the symbol of the VFT. + /// + /// # Returns + /// + /// The symbol as a `String`. + @query + Symbol() -> String; + } +} - events { - /// Emitted when a burn operation occurs. - BurnerTookPlace; - /// Emitted when a mint operation occurs. - MinterTookPlace; - /// Emitted when the allowance expiry period is changed. - ExpiryPeriodChanged: u32; - /// Emitted when the program exits. - Exited: actor_id; - /// Emitted when the contract is paused. - Paused; - /// Emitted when the contract is resumed. - Resumed; - } -}; +service VftNativeExchange@0x76ae7109177fcfab { + functions { + /// Burns `value` amount of VFT tokens and returns the equivalent amount of native value to the caller. + /// + /// # Arguments + /// + /// * `value` - The amount of VFT tokens to burn. + /// + /// # Returns + /// + /// A `CommandReply` containing the unit value `()` and transferring the native value. + Burn(value: U256) throws Error; + /// Burns all VFT tokens owned by the caller and returns the equivalent amount of native value. + /// + /// # Returns + /// + /// A `CommandReply` containing the unit value `()` and transferring the native value. + BurnAll() throws Error; + /// Mints VFT tokens to the caller equal to the amount of native value attached to the message. + /// + /// # Returns + /// + /// `Ok(())` on success. + Mint() throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + } +} -service VftExtension { - /// Allocates the next shard for allowances storage. - /// - /// Useful when the current shard is full. - /// - /// # Returns - /// - /// `true` if a new shard was allocated, `false` otherwise. - AllocateNextAllowancesShard : () -> bool; - /// Allocates the next shard for balances storage. - /// - /// Useful when the current shard is full. - /// - /// # Returns - /// - /// `true` if a new shard was allocated, `false` otherwise. - AllocateNextBalancesShard : () -> bool; - /// Removes an expired allowance. - /// - /// If the allowance from `owner` to `spender` has expired, it is removed to free up storage. - /// - /// # Arguments - /// - /// * `owner` - The account that granted the allowance. - /// * `spender` - The account that was granted the allowance. - /// - /// # Returns - /// - /// `true` if the allowance was removed, `false` otherwise (e.g., if it didn't exist). - RemoveExpiredAllowance : (owner: actor_id, spender: actor_id) -> bool; - /// Transfers the entire balance from the caller to `to`. - /// - /// # Arguments - /// - /// * `to` - The recipient of the tokens. - /// - /// # Returns - /// - /// `true` if any tokens were transferred. - TransferAll : (to: actor_id) -> bool; - /// Transfers the entire balance from `from` to `to` using the allowance mechanism. - /// - /// The caller must have sufficient allowance. - /// - /// # Arguments - /// - /// * `from` - The account to transfer tokens from. - /// * `to` - The recipient of the tokens. - /// - /// # Returns - /// - /// `true` if any tokens were transferred. - TransferAllFrom : (from: actor_id, to: actor_id) -> bool; - /// Returns the allowance detail (amount and expiration block) for a given owner and spender. - /// - /// # Arguments - /// - /// * `owner` - The account owning the tokens. - /// * `spender` - The account allowed to spend the tokens. - /// - /// # Returns - /// - /// An `Option` containing a tuple `(U256, u32)` representing the amount and expiration block height. - query AllowanceOf : (owner: actor_id, spender: actor_id) -> opt struct { u256, u32 }; - /// Returns a list of all allowances with pagination. - /// - /// # Arguments - /// - /// * `cursor` - The index to start from. - /// * `len` - The number of items to return. - /// - /// # Returns - /// - /// A vector of allowance details. - query Allowances : (cursor: u32, len: u32) -> vec struct { struct { actor_id, actor_id }, struct { u256, u32 } }; - /// Returns the balance of an account, if it exists in storage. - /// - /// Unlike `vft::balance_of` which returns 0 for non-existent accounts, this returns `None`. - /// - /// # Arguments - /// - /// * `account` - The account to query. - /// - /// # Returns - /// - /// An `Option` containing the balance. - query BalanceOf : (account: actor_id) -> opt u256; - /// Returns a list of all balances with pagination. - /// - /// # Arguments - /// - /// * `cursor` - The index to start from. - /// * `len` - The number of items to return. - /// - /// # Returns - /// - /// A vector of `(ActorId, U256)` pairs. - query Balances : (cursor: u32, len: u32) -> vec struct { actor_id, u256 }; - /// Returns the configured allowance expiry period. - query ExpiryPeriod : () -> u32; - /// Returns the amount of value (tokens) that are currently "unused" or reserved. - query UnusedValue : () -> u256; -}; - -service VftMetadata { - /// Returns the number of decimals used by the VFT. - /// - /// # Returns - /// - /// The decimals as a `u8`. - query Decimals : () -> u8; - /// Returns the name of the VFT. - /// - /// # Returns - /// - /// The name as a `String`. - query Name : () -> str; - /// Returns the symbol of the VFT. - /// - /// # Returns - /// - /// The symbol as a `String`. - query Symbol : () -> str; -}; - -service VftNativeExchange { - /// Burns `value` amount of VFT tokens and returns the equivalent amount of native value to the caller. - /// - /// # Arguments - /// - /// * `value` - The amount of VFT tokens to burn. - /// - /// # Returns - /// - /// A `CommandReply` containing the unit value `()` and transferring the native value. - Burn : (value: u256) -> null; - /// Burns all VFT tokens owned by the caller and returns the equivalent amount of native value. - /// - /// # Returns - /// - /// A `CommandReply` containing the unit value `()` and transferring the native value. - BurnAll : () -> null; - /// Mints VFT tokens to the caller equal to the amount of native value attached to the message. - /// - /// # Returns - /// - /// `Ok(())` on success. - Mint : () -> null; -}; - -service VftNativeExchangeAdmin { - /// Burns `value` amount of VFT tokens from `from` account and sends the equivalent - /// native value to `from`. - /// - /// This allows an admin (with burner role) to force an exchange/refund. - /// - /// # Arguments - /// - /// * `from` - The account to burn tokens from. - /// * `value` - The amount of tokens to burn. - /// - /// # Returns - /// - /// `Ok(())` on success. - BurnFrom : (from: actor_id, value: u256) -> null; - - events { - /// Emitted when re-minting tokens after a failed transfer fails. - FailedMint: struct { - to: actor_id, - value: u256, - }; - } -}; +service VftNativeExchangeAdmin@0x8c12232ffe097dd1 { + events { + /// Emitted when re-minting tokens after a failed transfer fails. + FailedMint { + to: ActorId, + value: U256, + }, + } + functions { + /// Burns `value` amount of VFT tokens from `from` account and sends the equivalent + /// native value to `from`. + /// + /// This allows an admin (with burner role) to force an exchange/refund. + /// + /// # Arguments + /// + /// * `from` - The account to burn tokens from. + /// * `value` - The amount of tokens to burn. + /// + /// # Returns + /// + /// `Ok(())` on success. + BurnFrom(from: ActorId, value: U256) throws Error; + } + types { + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + struct Error(String); + } +} +program AwesomeSailsTestClient { + constructors { + New(); + } + services { + Test@0x279e57efe3de371d, + AccessControl@0x26662bab76fe8d0f, + Vft@0x5036d76fe61f610b, + VftAdmin@0x6110108dc67c8d4f, + VftExtension@0xd25cc57e5a9cfb26, + VftMetadata@0x594f3407bfff54f1, + VftNativeExchange@0x76ae7109177fcfab, + VftNativeExchangeAdmin@0x8c12232ffe097dd1, + } +} diff --git a/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs b/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs index 2bf0906..a098291 100644 --- a/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs +++ b/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs @@ -2,7 +2,20 @@ #[allow(unused_imports)] use sails_rs::{client::*, collections::*, prelude::*}; pub struct AwesomeSailsTestClientProgram; + +impl AwesomeSailsTestClientProgram { + pub const ROUTE_ID_TEST: u8 = 1; + pub const ROUTE_ID_ACCESS_CONTROL: u8 = 2; + pub const ROUTE_ID_VFT: u8 = 3; + pub const ROUTE_ID_VFT_ADMIN: u8 = 4; + pub const ROUTE_ID_VFT_EXTENSION: u8 = 5; + pub const ROUTE_ID_VFT_METADATA: u8 = 6; + pub const ROUTE_ID_VFT_NATIVE_EXCHANGE: u8 = 7; + pub const ROUTE_ID_VFT_NATIVE_EXCHANGE_ADMIN: u8 = 8; +} + impl sails_rs::client::Program for AwesomeSailsTestClientProgram {} + pub trait AwesomeSailsTestClient { type Env: sails_rs::client::GearEnv; fn test(&self) -> sails_rs::client::Service; @@ -22,42 +35,43 @@ pub trait AwesomeSailsTestClient { &self, ) -> sails_rs::client::Service; } + impl AwesomeSailsTestClient for sails_rs::client::Actor { type Env = E; fn test(&self) -> sails_rs::client::Service { - self.service(stringify!(Test)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_TEST) } fn access_control( &self, ) -> sails_rs::client::Service { - self.service(stringify!(AccessControl)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_ACCESS_CONTROL) } fn vft(&self) -> sails_rs::client::Service { - self.service(stringify!(Vft)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_VFT) } fn vft_admin(&self) -> sails_rs::client::Service { - self.service(stringify!(VftAdmin)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_VFT_ADMIN) } fn vft_extension( &self, ) -> sails_rs::client::Service { - self.service(stringify!(VftExtension)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_VFT_EXTENSION) } fn vft_metadata(&self) -> sails_rs::client::Service { - self.service(stringify!(VftMetadata)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_VFT_METADATA) } fn vft_native_exchange( &self, ) -> sails_rs::client::Service { - self.service(stringify!(VftNativeExchange)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_VFT_NATIVE_EXCHANGE) } fn vft_native_exchange_admin( &self, ) -> sails_rs::client::Service { - self.service(stringify!(VftNativeExchangeAdmin)) + self.service(AwesomeSailsTestClientProgram::ROUTE_ID_VFT_NATIVE_EXCHANGE_ADMIN) } } pub trait AwesomeSailsTestClientCtors { @@ -68,6 +82,7 @@ pub trait AwesomeSailsTestClientCtors { self, ) -> sails_rs::client::PendingCtor; } + impl AwesomeSailsTestClientCtors for sails_rs::client::Deployment { @@ -81,11 +96,21 @@ impl AwesomeSailsTestClientCtors pub mod io { use super::*; - sails_rs::io_struct_impl!(New () -> ()); + sails_rs::io_struct_impl!(New () -> (), 0); } pub mod test { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + pub trait Test { type Env: sails_rs::client::GearEnv; fn set( @@ -95,7 +120,14 @@ pub mod test { expiry_period: u32, ) -> sails_rs::client::PendingCall; } + pub struct TestImpl; + + impl sails_rs::client::Identifiable for TestImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([39, 158, 87, 239, 227, 222, 55, 29]); + } + impl Test for sails_rs::client::Service { type Env = E; fn set( @@ -110,94 +142,35 @@ pub mod test { pub mod io { use super::*; - sails_rs::io_struct_impl!(Set (new_allowances: Vec<(ActorId,ActorId,U256,u32,)>, new_balances: Vec<(ActorId,U256,)>, expiry_period: u32) -> ()); + sails_rs::io_struct_impl!(Set (new_allowances: Vec<(ActorId, ActorId, U256, u32, )>, new_balances: Vec<(ActorId, U256, )>, expiry_period: u32) -> () | super::Error, 0, ::INTERFACE_ID); } } pub mod access_control { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + /// Pagination parameters for listing roles or members. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Pagination { + /// The number of items to skip. + pub offset: u32, + /// The maximum number of items to return. + pub limit: u32, + } + pub trait AccessControl { type Env: sails_rs::client::GearEnv; - /// Grants `role_id` to `target_account`. - /// - /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` - /// event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - fn grant_role( - &mut self, - role_id: [u8; 32], - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Grants `role_ids` to `target_account`. - /// - /// If `target_account` had not been already granted any of the `role_ids`, - /// emits a `RoleGranted` event for each newly granted role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - fn grant_roles_batch( - &mut self, - role_ids: Vec<[u8; 32]>, - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Revokes `role_id` from the calling account. - /// - /// Roles are often managed via `grant_role` and `revoke_role`: this function's - /// purpose is to provide a mechanism for accounts to lose their privileges - /// if they are compromised (such as when a trusted device is misplaced). - /// - /// If the calling account had been granted `role_id`, emits a `RoleRevoked` - /// event. - /// - /// # Requirements - /// - /// * The caller must be `account_id`. - fn renounce_role( - &mut self, - role_id: [u8; 32], - account_id: ActorId, - ) -> sails_rs::client::PendingCall; - /// Revokes `role_id` from `target_account`. - /// - /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - fn revoke_role( - &mut self, - role_id: [u8; 32], - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Revokes `role_ids` from `target_account`. - /// - /// If `target_account` had been granted any of the `role_ids`, - /// emits a `RoleRevoked` event for each newly revoked role. - /// - /// # Requirements - /// - /// * The caller must have the admin role for all specified `role_ids`. - fn revoke_roles_batch( - &mut self, - role_ids: Vec<[u8; 32]>, - target_account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Sets `new_admin_role_id` as the admin role for `role_id`. - /// - /// Emits a `RoleAdminChanged` event. - /// - /// # Requirements - /// - /// * The caller must have `role_id`'s admin role. - fn set_role_admin( - &mut self, - role_id: [u8; 32], - new_admin_role_id: [u8; 32], - ) -> sails_rs::client::PendingCall; /// Returns the number of roles assigned to the specified member. /// /// # Arguments @@ -262,6 +235,32 @@ pub mod access_control { &self, query: Option, ) -> sails_rs::client::PendingCall; + /// Grants `role_id` to `target_account`. + /// + /// If `target_account` had not been already granted `role_id`, emits a `RoleGranted` + /// event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. + fn grant_role( + &mut self, + role_id: [u8; 32], + target_account: ActorId, + ) -> sails_rs::client::PendingCall; + /// Grants `role_ids` to `target_account`. + /// + /// If `target_account` had not been already granted any of the `role_ids`, + /// emits a `RoleGranted` event for each newly granted role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. + fn grant_roles_batch( + &mut self, + role_ids: Vec<[u8; 32]>, + target_account: ActorId, + ) -> sails_rs::client::PendingCall; /// Checks if `account_id` has been granted `role_id`. /// /// # Arguments @@ -277,54 +276,73 @@ pub mod access_control { role_id: [u8; 32], account_id: ActorId, ) -> sails_rs::client::PendingCall; - } - pub struct AccessControlImpl; - impl AccessControl - for sails_rs::client::Service - { - type Env = E; - fn grant_role( - &mut self, - role_id: [u8; 32], - target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, target_account)) - } - fn grant_roles_batch( - &mut self, - role_ids: Vec<[u8; 32]>, - target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_ids, target_account)) - } + /// Revokes `role_id` from the calling account. + /// + /// Roles are often managed via `grant_role` and `revoke_role`: this function's + /// purpose is to provide a mechanism for accounts to lose their privileges + /// if they are compromised (such as when a trusted device is misplaced). + /// + /// If the calling account had been granted `role_id`, emits a `RoleRevoked` + /// event. + /// + /// # Requirements + /// + /// * The caller must be `account_id`. fn renounce_role( &mut self, role_id: [u8; 32], account_id: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, account_id)) - } + ) -> sails_rs::client::PendingCall; + /// Revokes `role_id` from `target_account`. + /// + /// If `target_account` had been granted `role_id`, emits a `RoleRevoked` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. fn revoke_role( &mut self, role_id: [u8; 32], target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, target_account)) - } + ) -> sails_rs::client::PendingCall; + /// Revokes `role_ids` from `target_account`. + /// + /// If `target_account` had been granted any of the `role_ids`, + /// emits a `RoleRevoked` event for each newly revoked role. + /// + /// # Requirements + /// + /// * The caller must have the admin role for all specified `role_ids`. fn revoke_roles_batch( &mut self, role_ids: Vec<[u8; 32]>, target_account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((role_ids, target_account)) - } + ) -> sails_rs::client::PendingCall; + /// Sets `new_admin_role_id` as the admin role for `role_id`. + /// + /// Emits a `RoleAdminChanged` event. + /// + /// # Requirements + /// + /// * The caller must have `role_id`'s admin role. fn set_role_admin( &mut self, role_id: [u8; 32], new_admin_role_id: [u8; 32], - ) -> sails_rs::client::PendingCall { - self.pending_call((role_id, new_admin_role_id)) - } + ) -> sails_rs::client::PendingCall; + } + + pub struct AccessControlImpl; + + impl sails_rs::client::Identifiable for AccessControlImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([38, 102, 43, 171, 118, 254, 141, 15]); + } + + impl AccessControl + for sails_rs::client::Service + { + type Env = E; fn get_member_role_count( &self, member_id: ActorId, @@ -366,6 +384,20 @@ pub mod access_control { ) -> sails_rs::client::PendingCall { self.pending_call((query,)) } + fn grant_role( + &mut self, + role_id: [u8; 32], + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, target_account)) + } + fn grant_roles_batch( + &mut self, + role_ids: Vec<[u8; 32]>, + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_ids, target_account)) + } fn has_role( &self, role_id: [u8; 32], @@ -373,56 +405,102 @@ pub mod access_control { ) -> sails_rs::client::PendingCall { self.pending_call((role_id, account_id)) } + fn renounce_role( + &mut self, + role_id: [u8; 32], + account_id: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, account_id)) + } + fn revoke_role( + &mut self, + role_id: [u8; 32], + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, target_account)) + } + fn revoke_roles_batch( + &mut self, + role_ids: Vec<[u8; 32]>, + target_account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((role_ids, target_account)) + } + fn set_role_admin( + &mut self, + role_id: [u8; 32], + new_admin_role_id: [u8; 32], + ) -> sails_rs::client::PendingCall { + self.pending_call((role_id, new_admin_role_id)) + } } pub mod io { use super::*; - sails_rs::io_struct_impl!(GrantRole (role_id: [u8; 32], target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(GrantRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(RenounceRole (role_id: [u8; 32], account_id: ActorId) -> ()); - sails_rs::io_struct_impl!(RevokeRole (role_id: [u8; 32], target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(RevokeRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> ()); - sails_rs::io_struct_impl!(SetRoleAdmin (role_id: [u8; 32], new_admin_role_id: [u8; 32]) -> ()); - sails_rs::io_struct_impl!(GetMemberRoleCount (member_id: ActorId) -> u32); - sails_rs::io_struct_impl!(GetMemberRoles (member_id: ActorId, query: Option) -> Vec<[u8; 32]>); - sails_rs::io_struct_impl!(GetRoleAdmin (role_id: [u8; 32]) -> [u8; 32]); - sails_rs::io_struct_impl!(GetRoleCount () -> u32); - sails_rs::io_struct_impl!(GetRoleMemberCount (role_id: [u8; 32]) -> u32); - sails_rs::io_struct_impl!(GetRoleMembers (role_id: [u8; 32], query: Option) -> Vec); - sails_rs::io_struct_impl!(GetRoles (query: Option) -> Vec<[u8; 32]>); - sails_rs::io_struct_impl!(HasRole (role_id: [u8; 32], account_id: ActorId) -> bool); + sails_rs::io_struct_impl!(GetMemberRoleCount (member_id: ActorId) -> u32, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetMemberRoles (member_id: ActorId, query: super::Option) -> Vec<[u8; 32]>, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleAdmin (role_id: [u8; 32]) -> [u8; 32], 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleCount () -> u32, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleMemberCount (role_id: [u8; 32]) -> u32, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoleMembers (role_id: [u8; 32], query: super::Option) -> Vec, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetRoles (query: super::Option) -> Vec<[u8; 32]>, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GrantRole (role_id: [u8; 32], target_account: ActorId) -> () | super::Error, 7, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GrantRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> () | super::Error, 8, ::INTERFACE_ID); + sails_rs::io_struct_impl!(HasRole (role_id: [u8; 32], account_id: ActorId) -> bool, 9, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RenounceRole (role_id: [u8; 32], account_id: ActorId) -> () | super::Error, 10, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RevokeRole (role_id: [u8; 32], target_account: ActorId) -> () | super::Error, 11, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RevokeRolesBatch (role_ids: Vec<[u8; 32]>, target_account: ActorId) -> () | super::Error, 12, ::INTERFACE_ID); + sails_rs::io_struct_impl!(SetRoleAdmin (role_id: [u8; 32], new_admin_role_id: [u8; 32]) -> () | super::Error, 13, ::INTERFACE_ID); } #[cfg(not(target_arch = "wasm32"))] pub mod events { use super::*; - #[derive(PartialEq, Debug, Encode, Decode)] + #[derive(PartialEq, Debug, Encode, Decode, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] + #[reflect_hash(crate = sails_rs)] pub enum AccessControlEvents { + /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. + #[codec(index = 0)] + RoleAdminChanged { + role_id: [u8; 32], + previous_admin_role_id: [u8; 32], + new_admin_role_id: [u8; 32], + sender: ActorId, + }, /// Emitted when `target_account` is granted `role_id`. + #[codec(index = 1)] RoleGranted { role_id: [u8; 32], target_account: ActorId, sender: ActorId, }, /// Emitted when `role_id` is revoked from `target_account`. + #[codec(index = 2)] RoleRevoked { role_id: [u8; 32], target_account: ActorId, sender: ActorId, }, - /// Emitted when `new_admin_role_id` is set as the admin role for `role_id`. - RoleAdminChanged { - role_id: [u8; 32], - previous_admin_role_id: [u8; 32], - new_admin_role_id: [u8; 32], - sender: ActorId, - }, } - impl sails_rs::client::Event for AccessControlEvents { - const EVENT_NAMES: &'static [Route] = - &["RoleGranted", "RoleRevoked", "RoleAdminChanged"]; + + impl AccessControlEvents { + pub fn entry_id(&self) -> u16 { + match self { + Self::RoleAdminChanged { .. } => 0, + Self::RoleGranted { .. } => 1, + Self::RoleRevoked { .. } => 2, + } + } } + + impl sails_rs::client::Event for AccessControlEvents {} + + impl sails_rs::client::Identifiable for AccessControlEvents { + const INTERFACE_ID: sails_rs::InterfaceId = + ::INTERFACE_ID; + } + impl sails_rs::client::ServiceWithEvents for AccessControlImpl { type Event = AccessControlEvents; } @@ -431,8 +509,33 @@ pub mod access_control { pub mod vft { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + pub trait Vft { type Env: sails_rs::client::GearEnv; + /// Returns the amount of tokens that `spender` is allowed to spend on behalf of `owner`. + /// + /// # Arguments + /// + /// * `owner` - The account that owns the tokens. + /// * `spender` - The account allowed to spend the tokens. + /// + /// # Returns + /// + /// The remaining allowance as `U256`. + fn allowance( + &self, + owner: ActorId, + spender: ActorId, + ) -> sails_rs::client::PendingCall; /// Approves `spender` to spend `value` amount of tokens on behalf of the caller. /// /// If `value` is `U256::MAX`, the allowance is treated as infinite. @@ -451,81 +554,80 @@ pub mod vft { spender: ActorId, value: U256, ) -> sails_rs::client::PendingCall; - /// Transfers `value` amount of tokens from the caller to `to`. - /// - /// Emits a `Transfer` event. + /// Returns the token balance of `account`. /// /// # Arguments /// - /// * `to` - The recipient of the tokens. - /// * `value` - The amount of tokens to transfer. + /// * `account` - The account to query the balance of. /// /// # Returns /// - /// `true` if the transfer was successful. - fn transfer( - &mut self, - to: ActorId, - value: U256, - ) -> sails_rs::client::PendingCall; - /// Transfers `value` amount of tokens from `from` to `to` using the allowance mechanism. + /// The balance as `U256`. + fn balance_of( + &self, + account: ActorId, + ) -> sails_rs::client::PendingCall; + /// Returns the total supply of tokens. + /// + /// # Returns + /// + /// The total supply as `U256`. + fn total_supply(&self) -> sails_rs::client::PendingCall; + /// Transfers `value` amount of tokens from the caller to `to`. /// - /// The caller (spender) must have sufficient allowance from `from` (owner). /// Emits a `Transfer` event. /// /// # Arguments /// - /// * `from` - The account to transfer tokens from. /// * `to` - The recipient of the tokens. /// * `value` - The amount of tokens to transfer. /// /// # Returns /// /// `true` if the transfer was successful. - fn transfer_from( + fn transfer( &mut self, - from: ActorId, to: ActorId, value: U256, - ) -> sails_rs::client::PendingCall; - /// Returns the amount of tokens that `spender` is allowed to spend on behalf of `owner`. - /// - /// # Arguments - /// - /// * `owner` - The account that owns the tokens. - /// * `spender` - The account allowed to spend the tokens. - /// - /// # Returns - /// - /// The remaining allowance as `U256`. - fn allowance( - &self, - owner: ActorId, - spender: ActorId, - ) -> sails_rs::client::PendingCall; - /// Returns the token balance of `account`. - /// - /// # Arguments + ) -> sails_rs::client::PendingCall; + /// Transfers `value` amount of tokens from `from` to `to` using the allowance mechanism. /// - /// * `account` - The account to query the balance of. + /// The caller (spender) must have sufficient allowance from `from` (owner). + /// Emits a `Transfer` event. /// - /// # Returns + /// # Arguments /// - /// The balance as `U256`. - fn balance_of( - &self, - account: ActorId, - ) -> sails_rs::client::PendingCall; - /// Returns the total supply of tokens. + /// * `from` - The account to transfer tokens from. + /// * `to` - The recipient of the tokens. + /// * `value` - The amount of tokens to transfer. /// /// # Returns /// - /// The total supply as `U256`. - fn total_supply(&self) -> sails_rs::client::PendingCall; + /// `true` if the transfer was successful. + fn transfer_from( + &mut self, + from: ActorId, + to: ActorId, + value: U256, + ) -> sails_rs::client::PendingCall; } + pub struct VftImpl; + + impl sails_rs::client::Identifiable for VftImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([80, 54, 215, 111, 230, 31, 97, 11]); + } + impl Vft for sails_rs::client::Service { type Env = E; + fn allowance( + &self, + owner: ActorId, + spender: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((owner, spender)) + } fn approve( &mut self, spender: ActorId, @@ -533,6 +635,15 @@ pub mod vft { ) -> sails_rs::client::PendingCall { self.pending_call((spender, value)) } + fn balance_of( + &self, + account: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((account,)) + } + fn total_supply(&self) -> sails_rs::client::PendingCall { + self.pending_call(()) + } fn transfer( &mut self, to: ActorId, @@ -548,56 +659,57 @@ pub mod vft { ) -> sails_rs::client::PendingCall { self.pending_call((from, to, value)) } - fn allowance( - &self, - owner: ActorId, - spender: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((owner, spender)) - } - fn balance_of( - &self, - account: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((account,)) - } - fn total_supply(&self) -> sails_rs::client::PendingCall { - self.pending_call(()) - } } pub mod io { use super::*; - sails_rs::io_struct_impl!(Approve (spender: ActorId, value: U256) -> bool); - sails_rs::io_struct_impl!(Transfer (to: ActorId, value: U256) -> bool); - sails_rs::io_struct_impl!(TransferFrom (from: ActorId, to: ActorId, value: U256) -> bool); - sails_rs::io_struct_impl!(Allowance (owner: ActorId, spender: ActorId) -> U256); - sails_rs::io_struct_impl!(BalanceOf (account: ActorId) -> U256); - sails_rs::io_struct_impl!(TotalSupply () -> U256); + sails_rs::io_struct_impl!(Allowance (owner: ActorId, spender: ActorId) -> U256 | super::Error, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Approve (spender: ActorId, value: U256) -> bool | super::Error, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BalanceOf (account: ActorId) -> U256 | super::Error, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(TotalSupply () -> U256 | super::Error, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Transfer (to: ActorId, value: U256) -> bool | super::Error, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(TransferFrom (from: ActorId, to: ActorId, value: U256) -> bool | super::Error, 5, ::INTERFACE_ID); } #[cfg(not(target_arch = "wasm32"))] pub mod events { use super::*; - #[derive(PartialEq, Debug, Encode, Decode)] + #[derive(PartialEq, Debug, Encode, Decode, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] + #[reflect_hash(crate = sails_rs)] pub enum VftEvents { /// Emitted when an approval is granted or updated. + #[codec(index = 0)] Approval { owner: ActorId, spender: ActorId, value: U256, }, /// Emitted when tokens are transferred. + #[codec(index = 1)] Transfer { from: ActorId, to: ActorId, value: U256, }, } - impl sails_rs::client::Event for VftEvents { - const EVENT_NAMES: &'static [Route] = &["Approval", "Transfer"]; + + impl VftEvents { + pub fn entry_id(&self) -> u16 { + match self { + Self::Approval { .. } => 0, + Self::Transfer { .. } => 1, + } + } + } + + impl sails_rs::client::Event for VftEvents {} + + impl sails_rs::client::Identifiable for VftEvents { + const INTERFACE_ID: sails_rs::InterfaceId = + ::INTERFACE_ID; } + impl sails_rs::client::ServiceWithEvents for VftImpl { type Event = VftEvents; } @@ -606,6 +718,16 @@ pub mod vft { pub mod vft_admin { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + pub trait VftAdmin { type Env: sails_rs::client::GearEnv; /// Appends a new shard to the allowances storage map. @@ -669,6 +791,8 @@ pub mod vft_admin { &mut self, inheritor: ActorId, ) -> sails_rs::client::PendingCall; + /// Returns `true` if the contract is paused. + fn is_paused(&self) -> sails_rs::client::PendingCall; /// Mints `value` tokens to `to` account. /// /// # Requirements @@ -703,10 +827,15 @@ pub mod vft_admin { &mut self, period: u32, ) -> sails_rs::client::PendingCall; - /// Returns `true` if the contract is paused. - fn is_paused(&self) -> sails_rs::client::PendingCall; } + pub struct VftAdminImpl; + + impl sails_rs::client::Identifiable for VftAdminImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([97, 16, 16, 141, 198, 124, 141, 79]); + } + impl VftAdmin for sails_rs::client::Service { type Env = E; fn append_allowances_shard( @@ -742,6 +871,9 @@ pub mod vft_admin { ) -> sails_rs::client::PendingCall { self.pending_call((inheritor,)) } + fn is_paused(&self) -> sails_rs::client::PendingCall { + self.pending_call(()) + } fn mint( &mut self, to: ActorId, @@ -761,50 +893,69 @@ pub mod vft_admin { ) -> sails_rs::client::PendingCall { self.pending_call((period,)) } - fn is_paused(&self) -> sails_rs::client::PendingCall { - self.pending_call(()) - } } pub mod io { use super::*; - sails_rs::io_struct_impl!(AppendAllowancesShard (capacity: u32) -> ()); - sails_rs::io_struct_impl!(AppendBalancesShard (capacity: u32) -> ()); - sails_rs::io_struct_impl!(ApproveFrom (owner: ActorId, spender: ActorId, value: U256) -> bool); - sails_rs::io_struct_impl!(Burn (from: ActorId, value: U256) -> ()); - sails_rs::io_struct_impl!(Exit (inheritor: ActorId) -> ()); - sails_rs::io_struct_impl!(Mint (to: ActorId, value: U256) -> ()); - sails_rs::io_struct_impl!(Pause () -> ()); - sails_rs::io_struct_impl!(Resume () -> ()); - sails_rs::io_struct_impl!(SetExpiryPeriod (period: u32) -> ()); - sails_rs::io_struct_impl!(IsPaused () -> bool); + sails_rs::io_struct_impl!(AppendAllowancesShard (capacity: u32) -> () | super::Error, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(AppendBalancesShard (capacity: u32) -> () | super::Error, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(ApproveFrom (owner: ActorId, spender: ActorId, value: U256) -> bool | super::Error, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Burn (from: ActorId, value: U256) -> () | super::Error, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Exit (inheritor: ActorId) -> () | super::Error, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(IsPaused () -> bool, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Mint (to: ActorId, value: U256) -> () | super::Error, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Pause () -> () | super::Error, 7, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Resume () -> () | super::Error, 8, ::INTERFACE_ID); + sails_rs::io_struct_impl!(SetExpiryPeriod (period: u32) -> () | super::Error, 9, ::INTERFACE_ID); } #[cfg(not(target_arch = "wasm32"))] pub mod events { use super::*; - #[derive(PartialEq, Debug, Encode, Decode)] + #[derive(PartialEq, Debug, Encode, Decode, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] + #[reflect_hash(crate = sails_rs)] pub enum VftAdminEvents { + /// Emitted when a burn operation occurs. + #[codec(index = 0)] BurnerTookPlace, - MinterTookPlace, - /// Emitted when the allowance expiry period is changed. - ExpiryPeriodChanged(u32), /// Emitted when the program exits. + #[codec(index = 1)] Exited(ActorId), + /// Emitted when the allowance expiry period is changed. + #[codec(index = 2)] + ExpiryPeriodChanged(u32), + /// Emitted when a mint operation occurs. + #[codec(index = 3)] + MinterTookPlace, + /// Emitted when the contract is paused. + #[codec(index = 4)] Paused, + /// Emitted when the contract is resumed. + #[codec(index = 5)] Resumed, } - impl sails_rs::client::Event for VftAdminEvents { - const EVENT_NAMES: &'static [Route] = &[ - "BurnerTookPlace", - "MinterTookPlace", - "ExpiryPeriodChanged", - "Exited", - "Paused", - "Resumed", - ]; + + impl VftAdminEvents { + pub fn entry_id(&self) -> u16 { + match self { + Self::BurnerTookPlace { .. } => 0, + Self::Exited { .. } => 1, + Self::ExpiryPeriodChanged { .. } => 2, + Self::MinterTookPlace { .. } => 3, + Self::Paused { .. } => 4, + Self::Resumed { .. } => 5, + } + } + } + + impl sails_rs::client::Event for VftAdminEvents {} + + impl sails_rs::client::Identifiable for VftAdminEvents { + const INTERFACE_ID: sails_rs::InterfaceId = + ::INTERFACE_ID; } + impl sails_rs::client::ServiceWithEvents for VftAdminImpl { type Event = VftAdminEvents; } @@ -813,6 +964,16 @@ pub mod vft_admin { pub mod vft_extension { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + pub trait VftExtension { type Env: sails_rs::client::GearEnv; /// Allocates the next shard for allowances storage. @@ -835,53 +996,6 @@ pub mod vft_extension { fn allocate_next_balances_shard( &mut self, ) -> sails_rs::client::PendingCall; - /// Removes an expired allowance. - /// - /// If the allowance from `owner` to `spender` has expired, it is removed to free up storage. - /// - /// # Arguments - /// - /// * `owner` - The account that granted the allowance. - /// * `spender` - The account that was granted the allowance. - /// - /// # Returns - /// - /// `true` if the allowance was removed, `false` otherwise (e.g., if it didn't exist). - fn remove_expired_allowance( - &mut self, - owner: ActorId, - spender: ActorId, - ) -> sails_rs::client::PendingCall; - /// Transfers the entire balance from the caller to `to`. - /// - /// # Arguments - /// - /// * `to` - The recipient of the tokens. - /// - /// # Returns - /// - /// `true` if any tokens were transferred. - fn transfer_all( - &mut self, - to: ActorId, - ) -> sails_rs::client::PendingCall; - /// Transfers the entire balance from `from` to `to` using the allowance mechanism. - /// - /// The caller must have sufficient allowance. - /// - /// # Arguments - /// - /// * `from` - The account to transfer tokens from. - /// * `to` - The recipient of the tokens. - /// - /// # Returns - /// - /// `true` if any tokens were transferred. - fn transfer_all_from( - &mut self, - from: ActorId, - to: ActorId, - ) -> sails_rs::client::PendingCall; /// Returns the allowance detail (amount and expiration block) for a given owner and spender. /// /// # Arguments @@ -944,10 +1058,64 @@ pub mod vft_extension { ) -> sails_rs::client::PendingCall; /// Returns the configured allowance expiry period. fn expiry_period(&self) -> sails_rs::client::PendingCall; + /// Removes an expired allowance. + /// + /// If the allowance from `owner` to `spender` has expired, it is removed to free up storage. + /// + /// # Arguments + /// + /// * `owner` - The account that granted the allowance. + /// * `spender` - The account that was granted the allowance. + /// + /// # Returns + /// + /// `true` if the allowance was removed, `false` otherwise (e.g., if it didn't exist). + fn remove_expired_allowance( + &mut self, + owner: ActorId, + spender: ActorId, + ) -> sails_rs::client::PendingCall; + /// Transfers the entire balance from the caller to `to`. + /// + /// # Arguments + /// + /// * `to` - The recipient of the tokens. + /// + /// # Returns + /// + /// `true` if any tokens were transferred. + fn transfer_all( + &mut self, + to: ActorId, + ) -> sails_rs::client::PendingCall; + /// Transfers the entire balance from `from` to `to` using the allowance mechanism. + /// + /// The caller must have sufficient allowance. + /// + /// # Arguments + /// + /// * `from` - The account to transfer tokens from. + /// * `to` - The recipient of the tokens. + /// + /// # Returns + /// + /// `true` if any tokens were transferred. + fn transfer_all_from( + &mut self, + from: ActorId, + to: ActorId, + ) -> sails_rs::client::PendingCall; /// Returns the amount of value (tokens) that are currently "unused" or reserved. fn unused_value(&self) -> sails_rs::client::PendingCall; } + pub struct VftExtensionImpl; + + impl sails_rs::client::Identifiable for VftExtensionImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([210, 92, 197, 126, 90, 156, 251, 38]); + } + impl VftExtension for sails_rs::client::Service { type Env = E; fn allocate_next_allowances_shard( @@ -960,26 +1128,6 @@ pub mod vft_extension { ) -> sails_rs::client::PendingCall { self.pending_call(()) } - fn remove_expired_allowance( - &mut self, - owner: ActorId, - spender: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((owner, spender)) - } - fn transfer_all( - &mut self, - to: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((to,)) - } - fn transfer_all_from( - &mut self, - from: ActorId, - to: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((from, to)) - } fn allowance_of( &self, owner: ActorId, @@ -1010,6 +1158,26 @@ pub mod vft_extension { fn expiry_period(&self) -> sails_rs::client::PendingCall { self.pending_call(()) } + fn remove_expired_allowance( + &mut self, + owner: ActorId, + spender: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((owner, spender)) + } + fn transfer_all( + &mut self, + to: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((to,)) + } + fn transfer_all_from( + &mut self, + from: ActorId, + to: ActorId, + ) -> sails_rs::client::PendingCall { + self.pending_call((from, to)) + } fn unused_value(&self) -> sails_rs::client::PendingCall { self.pending_call(()) } @@ -1017,22 +1185,23 @@ pub mod vft_extension { pub mod io { use super::*; - sails_rs::io_struct_impl!(AllocateNextAllowancesShard () -> bool); - sails_rs::io_struct_impl!(AllocateNextBalancesShard () -> bool); - sails_rs::io_struct_impl!(RemoveExpiredAllowance (owner: ActorId, spender: ActorId) -> bool); - sails_rs::io_struct_impl!(TransferAll (to: ActorId) -> bool); - sails_rs::io_struct_impl!(TransferAllFrom (from: ActorId, to: ActorId) -> bool); - sails_rs::io_struct_impl!(AllowanceOf (owner: ActorId, spender: ActorId) -> Option<(U256,u32,)>); - sails_rs::io_struct_impl!(Allowances (cursor: u32, len: u32) -> Vec<((ActorId,ActorId,),(U256,u32,),)>); - sails_rs::io_struct_impl!(BalanceOf (account: ActorId) -> Option); - sails_rs::io_struct_impl!(Balances (cursor: u32, len: u32) -> Vec<(ActorId,U256,)>); - sails_rs::io_struct_impl!(ExpiryPeriod () -> u32); - sails_rs::io_struct_impl!(UnusedValue () -> U256); + sails_rs::io_struct_impl!(AllocateNextAllowancesShard () -> bool | super::Error, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(AllocateNextBalancesShard () -> bool | super::Error, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(AllowanceOf (owner: ActorId, spender: ActorId) -> super::Option<(U256, u32, ), > | super::Error, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Allowances (cursor: u32, len: u32) -> Vec<((ActorId, ActorId, ), (U256, u32, ), )> | super::Error, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BalanceOf (account: ActorId) -> super::Option | super::Error, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Balances (cursor: u32, len: u32) -> Vec<(ActorId, U256, )> | super::Error, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(ExpiryPeriod () -> u32 | super::Error, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RemoveExpiredAllowance (owner: ActorId, spender: ActorId) -> bool | super::Error, 7, ::INTERFACE_ID); + sails_rs::io_struct_impl!(TransferAll (to: ActorId) -> bool | super::Error, 8, ::INTERFACE_ID); + sails_rs::io_struct_impl!(TransferAllFrom (from: ActorId, to: ActorId) -> bool | super::Error, 9, ::INTERFACE_ID); + sails_rs::io_struct_impl!(UnusedValue () -> U256 | super::Error, 10, ::INTERFACE_ID); } } pub mod vft_metadata { use super::*; + pub trait VftMetadata { type Env: sails_rs::client::GearEnv; /// Returns the number of decimals used by the VFT. @@ -1054,7 +1223,14 @@ pub mod vft_metadata { /// The symbol as a `String`. fn symbol(&self) -> sails_rs::client::PendingCall; } + pub struct VftMetadataImpl; + + impl sails_rs::client::Identifiable for VftMetadataImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([89, 79, 52, 7, 191, 255, 84, 241]); + } + impl VftMetadata for sails_rs::client::Service { type Env = E; fn decimals(&self) -> sails_rs::client::PendingCall { @@ -1070,14 +1246,24 @@ pub mod vft_metadata { pub mod io { use super::*; - sails_rs::io_struct_impl!(Decimals () -> u8); - sails_rs::io_struct_impl!(Name () -> String); - sails_rs::io_struct_impl!(Symbol () -> String); + sails_rs::io_struct_impl!(Decimals () -> u8, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Name () -> String, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Symbol () -> String, 2, ::INTERFACE_ID); } } pub mod vft_native_exchange { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + pub trait VftNativeExchange { type Env: sails_rs::client::GearEnv; /// Burns `value` amount of VFT tokens and returns the equivalent amount of native value to the caller. @@ -1103,7 +1289,14 @@ pub mod vft_native_exchange { /// `Ok(())` on success. fn mint(&mut self) -> sails_rs::client::PendingCall; } + pub struct VftNativeExchangeImpl; + + impl sails_rs::client::Identifiable for VftNativeExchangeImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([118, 174, 113, 9, 23, 127, 207, 171]); + } + impl VftNativeExchange for sails_rs::client::Service { @@ -1121,14 +1314,24 @@ pub mod vft_native_exchange { pub mod io { use super::*; - sails_rs::io_struct_impl!(Burn (value: U256) -> ()); - sails_rs::io_struct_impl!(BurnAll () -> ()); - sails_rs::io_struct_impl!(Mint () -> ()); + sails_rs::io_struct_impl!(Burn (value: U256) -> () | super::Error, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BurnAll () -> () | super::Error, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Mint () -> () | super::Error, 2, ::INTERFACE_ID); } } pub mod vft_native_exchange_admin { use super::*; + + /// Represents a generic error type within the `awesome-sails` ecosystem. + /// + /// This struct wraps a string message providing details about the error. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Error(pub String); + pub trait VftNativeExchangeAdmin { type Env: sails_rs::client::GearEnv; /// Burns `value` amount of VFT tokens from `from` account and sends the equivalent @@ -1150,7 +1353,14 @@ pub mod vft_native_exchange_admin { value: U256, ) -> sails_rs::client::PendingCall; } + pub struct VftNativeExchangeAdminImpl; + + impl sails_rs::client::Identifiable for VftNativeExchangeAdminImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([140, 18, 35, 47, 254, 9, 125, 209]); + } + impl VftNativeExchangeAdmin for sails_rs::client::Service { @@ -1166,33 +1376,38 @@ pub mod vft_native_exchange_admin { pub mod io { use super::*; - sails_rs::io_struct_impl!(BurnFrom (from: ActorId, value: U256) -> ()); + sails_rs::io_struct_impl!(BurnFrom (from: ActorId, value: U256) -> () | super::Error, 0, ::INTERFACE_ID); } #[cfg(not(target_arch = "wasm32"))] pub mod events { use super::*; - #[derive(PartialEq, Debug, Encode, Decode)] + #[derive(PartialEq, Debug, Encode, Decode, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] + #[reflect_hash(crate = sails_rs)] pub enum VftNativeExchangeAdminEvents { /// Emitted when re-minting tokens after a failed transfer fails. + #[codec(index = 0)] FailedMint { to: ActorId, value: U256 }, } - impl sails_rs::client::Event for VftNativeExchangeAdminEvents { - const EVENT_NAMES: &'static [Route] = &["FailedMint"]; + + impl VftNativeExchangeAdminEvents { + pub fn entry_id(&self) -> u16 { + match self { + Self::FailedMint { .. } => 0, + } + } + } + + impl sails_rs::client::Event for VftNativeExchangeAdminEvents {} + + impl sails_rs::client::Identifiable for VftNativeExchangeAdminEvents { + const INTERFACE_ID: sails_rs::InterfaceId = + ::INTERFACE_ID; } + impl sails_rs::client::ServiceWithEvents for VftNativeExchangeAdminImpl { type Event = VftNativeExchangeAdminEvents; } } } -/// Pagination parameters for listing roles or members. -#[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo)] -#[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] -pub struct Pagination { - /// The number of items to skip. - pub offset: u32, - /// The maximum number of items to return. - pub limit: u32, -} diff --git a/tests/msg-tracker-test/app/src/lib.rs b/tests/msg-tracker-test/app/src/lib.rs index db18496..f6cd0df 100644 --- a/tests/msg-tracker-test/app/src/lib.rs +++ b/tests/msg-tracker-test/app/src/lib.rs @@ -7,17 +7,19 @@ use awesome_sails::msg_tracker::{ use awesome_sails_storage::StorageRefCell; use sails_rs::{cell::RefCell, prelude::*}; -#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)] +#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub enum OpStatus { Pending, Completed, } -#[derive(Debug, Decode, Encode, TypeInfo, PartialEq)] +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] #[scale_info(crate = sails_rs::scale_info)] +#[reflect_hash(crate = sails_rs)] pub enum CounterError { OperationNotFound, AlreadyCompleted, diff --git a/tests/msg-tracker-test/app/tests/common/mod.rs b/tests/msg-tracker-test/app/tests/common/mod.rs index a4c7b71..4aa30ac 100644 --- a/tests/msg-tracker-test/app/tests/common/mod.rs +++ b/tests/msg-tracker-test/app/tests/common/mod.rs @@ -31,23 +31,7 @@ pub async fn deploy_program() -> (Actor, .deploy::(code_id, b"salt".to_vec()) .new() .await - .expect("failed to deploy program"); + .unwrap(); (program, env) } - -#[track_caller] -pub fn assert_str_panic(e: GtestError, exp: &str) { - match e { - GtestError::ReplyHasError( - ErrorReplyReason::Execution(SimpleExecutionError::UserspacePanic), - res, - ) => { - let actual = String::from_utf8_lossy(&res); - let expected = - format!("panicked with 'called `Result::unwrap()` on an `Err` value: {exp}'"); - assert_eq!(actual, expected); - } - _ => core::panic!("not an expected error reply type: {e:?}"), - } -} diff --git a/tests/msg-tracker-test/app/tests/gtest.rs b/tests/msg-tracker-test/app/tests/gtest.rs index 9cd7460..46e8539 100644 --- a/tests/msg-tracker-test/app/tests/gtest.rs +++ b/tests/msg-tracker-test/app/tests/gtest.rs @@ -1,9 +1,10 @@ mod common; -use common::{assert_str_panic, deploy_program}; +use common::deploy_program; use msg_tracker_test_client::{ - MsgTrackerTestClient, OpStatus, dynamic_counter::DynamicCounter as _, - fixed_counter::FixedCounter as _, + MsgTrackerTestClient, + dynamic_counter::{self, DynamicCounter as _}, + fixed_counter::{self, FixedCounter as _, TrackerError}, }; use sails_rs::prelude::*; @@ -16,11 +17,11 @@ async fn test_fixed_counter_overflow_panic() { fixed_counter.request_increment().await.unwrap(); } - let res = fixed_counter.request_increment().await; + let res = fixed_counter.request_increment().await.unwrap(); assert!(res.is_err()); if let Err(e) = res { - assert_str_panic(e, "CapacityExceeded"); + assert_eq!(e, TrackerError::CapacityExceeded); } } @@ -31,16 +32,18 @@ async fn test_dynamic_counter_flow() { let msg_id = dynamic_counter.request_increment().await.unwrap(); - let status = dynamic_counter.get_status(msg_id).await.unwrap(); - assert_eq!(status, Some(OpStatus::Pending)); + let status = dynamic_counter.get_status(msg_id).await.unwrap().unwrap(); + assert_eq!(status, dynamic_counter::OpStatus::Pending); - dynamic_counter.confirm_increment(msg_id).await.unwrap(); + dynamic_counter + .confirm_increment(msg_id) + .await + .unwrap() + .unwrap(); assert_eq!(dynamic_counter.get_val().await.unwrap(), 1); - assert_eq!( - dynamic_counter.get_status(msg_id).await.unwrap(), - Some(OpStatus::Completed) - ); + let status = dynamic_counter.get_status(msg_id).await.unwrap().unwrap(); + assert_eq!(status, dynamic_counter::OpStatus::Completed); } #[tokio::test] @@ -50,16 +53,16 @@ async fn test_fixed_counter_removal_and_reuse() { let mut last_id = MessageId::zero(); for _ in 0..5 { - last_id = fixed_counter.request_increment().await.unwrap(); + last_id = fixed_counter.request_increment().await.unwrap().unwrap(); } - let res = fixed_counter.request_increment().await; + let res = fixed_counter.request_increment().await.unwrap(); assert!(res.is_err()); - let removed = fixed_counter.remove_fixed(last_id).await.unwrap(); - assert_eq!(removed, Some(OpStatus::Pending)); + let removed = fixed_counter.remove_fixed(last_id).await.unwrap().unwrap(); + assert_eq!(removed, fixed_counter::OpStatus::Pending); - fixed_counter.request_increment().await.unwrap(); + fixed_counter.request_increment().await.unwrap().unwrap(); } #[tokio::test] @@ -69,7 +72,7 @@ async fn test_update_non_existent() { let random_id = MessageId::from([1u8; 32]); let updated = dynamic_counter - .update_dynamic(random_id, OpStatus::Completed) + .update_dynamic(random_id, dynamic_counter::OpStatus::Completed) .await .unwrap(); assert!(!updated); @@ -80,27 +83,27 @@ async fn test_fixed_storage_mixed_workload() { let (program, _env) = deploy_program().await; let mut fixed_counter = program.fixed_counter(); - let id1 = fixed_counter.request_increment().await.unwrap(); - let id2 = fixed_counter.request_increment().await.unwrap(); - let _id3 = fixed_counter.request_increment().await.unwrap(); + let id1 = fixed_counter.request_increment().await.unwrap().unwrap(); + let id2 = fixed_counter.request_increment().await.unwrap().unwrap(); + let _id3 = fixed_counter.request_increment().await.unwrap().unwrap(); let updated = fixed_counter - .update_fixed(id2, OpStatus::Completed) + .update_fixed(id2, fixed_counter::OpStatus::Completed) .await .unwrap(); assert!(updated); assert_eq!( - fixed_counter.get_status(id2).await.unwrap(), - Some(OpStatus::Completed) + fixed_counter.get_status(id2).await.unwrap().unwrap(), + fixed_counter::OpStatus::Completed ); - fixed_counter.remove_fixed(id1).await.unwrap(); + fixed_counter.remove_fixed(id1).await.unwrap().unwrap(); - fixed_counter.request_increment().await.unwrap(); - fixed_counter.request_increment().await.unwrap(); - fixed_counter.request_increment().await.unwrap(); + fixed_counter.request_increment().await.unwrap().unwrap(); + fixed_counter.request_increment().await.unwrap().unwrap(); + fixed_counter.request_increment().await.unwrap().unwrap(); - assert!(fixed_counter.request_increment().await.is_err()); + assert!(fixed_counter.request_increment().await.unwrap().is_err()); } #[tokio::test] @@ -109,14 +112,14 @@ async fn test_clear_and_reutilize() { let mut fixed_counter = program.fixed_counter(); for _ in 0..5 { - fixed_counter.request_increment().await.unwrap(); + fixed_counter.request_increment().await.unwrap().unwrap(); } - assert!(fixed_counter.request_increment().await.is_err()); + assert!(fixed_counter.request_increment().await.unwrap().is_err()); fixed_counter.clear_fixed().await.unwrap(); for _ in 0..5 { - fixed_counter.request_increment().await.unwrap(); + fixed_counter.request_increment().await.unwrap().unwrap(); } } @@ -127,14 +130,14 @@ async fn test_pagination_logic() { let mut ids = Vec::new(); for _ in 0..5 { - ids.push(fixed_counter.request_increment().await.unwrap()); + ids.push(fixed_counter.request_increment().await.unwrap().unwrap()); } let all = fixed_counter.get_statuses(None).await.unwrap(); assert_eq!(all.len(), 5); let page = fixed_counter - .get_statuses(Some(msg_tracker_test_client::Pagination { + .get_statuses(Some(fixed_counter::Pagination { offset: 2, limit: 2, })) diff --git a/tests/msg-tracker-test/client/msg_tracker_test_client.idl b/tests/msg-tracker-test/client/msg_tracker_test_client.idl index 68a4fe8..9f919b4 100644 --- a/tests/msg-tracker-test/client/msg_tracker_test_client.idl +++ b/tests/msg-tracker-test/client/msg_tracker_test_client.idl @@ -1,38 +1,77 @@ -type OpStatus = enum { - Pending, - Completed, -}; -/// Pagination parameters for listing tracked messages. -type Pagination = struct { - /// The number of items to skip. - offset: u32, - /// The maximum number of items to return. - limit: u32, -}; +!@sails: 1.0.0-beta.2 -constructor { - New : (); -}; +service DynamicCounter@0xd4734c64d557b419 { + functions { + ClearDynamic(); + ConfirmIncrement(id: MessageId) throws CounterError; + @query + GetStatus(id: MessageId) -> Option; + @query + GetStatuses(query: Option) -> [(MessageId, OpStatus)]; + @query + GetVal() -> u32; + RequestIncrement() -> MessageId; + UpdateDynamic(id: MessageId, status: OpStatus) -> bool; + } + types { + enum CounterError { + OperationNotFound, + AlreadyCompleted, + } + enum OpStatus { + Pending, + Completed, + } + /// Pagination parameters for listing tracked messages. + struct Pagination { + /// The number of items to skip. + offset: u32, + /// The maximum number of items to return. + limit: u32, + } + } +} -service DynamicCounter { - ClearDynamic : () -> null; - ConfirmIncrement : (id: message_id) -> null; - RequestIncrement : () -> message_id; - UpdateDynamic : (id: message_id, status: OpStatus) -> bool; - query GetStatus : (id: message_id) -> opt OpStatus; - query GetStatuses : (query: opt Pagination) -> vec struct { message_id, OpStatus }; - query GetVal : () -> u32; -}; - -service FixedCounter { - ClearFixed : () -> null; - ConfirmIncrement : (id: message_id) -> bool; - RemoveFixed : (id: message_id) -> opt OpStatus; - RequestIncrement : () -> message_id; - UpdateFixed : (id: message_id, status: OpStatus) -> bool; - query GetStatus : (id: message_id) -> opt OpStatus; - query GetStatuses : (query: opt Pagination) -> vec struct { message_id, OpStatus }; - query GetVal : () -> u32; -}; +service FixedCounter@0xcb6f2fb17b6296e6 { + functions { + ClearFixed(); + ConfirmIncrement(id: MessageId) -> bool; + @query + GetStatus(id: MessageId) -> Option; + @query + GetStatuses(query: Option) -> [(MessageId, OpStatus)]; + @query + GetVal() -> u32; + RemoveFixed(id: MessageId) -> Option; + RequestIncrement() -> MessageId throws TrackerError; + UpdateFixed(id: MessageId, status: OpStatus) -> bool; + } + types { + enum OpStatus { + Pending, + Completed, + } + /// Pagination parameters for listing tracked messages. + struct Pagination { + /// The number of items to skip. + offset: u32, + /// The maximum number of items to return. + limit: u32, + } + enum TrackerError { + /// Indicates that the fixed storage capacity has been exceeded. + CapacityExceeded, + } + } +} +program MsgTrackerTestClient { + constructors { + New(); + } + services { + DynamicCounter@0xd4734c64d557b419, + FixedCounter@0xcb6f2fb17b6296e6, + } +} diff --git a/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs b/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs index 2d63d30..a8ef84f 100644 --- a/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs +++ b/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs @@ -2,7 +2,14 @@ #[allow(unused_imports)] use sails_rs::{client::*, collections::*, prelude::*}; pub struct MsgTrackerTestClientProgram; + +impl MsgTrackerTestClientProgram { + pub const ROUTE_ID_DYNAMIC_COUNTER: u8 = 1; + pub const ROUTE_ID_FIXED_COUNTER: u8 = 2; +} + impl sails_rs::client::Program for MsgTrackerTestClientProgram {} + pub trait MsgTrackerTestClient { type Env: sails_rs::client::GearEnv; fn dynamic_counter( @@ -12,6 +19,7 @@ pub trait MsgTrackerTestClient { &self, ) -> sails_rs::client::Service; } + impl MsgTrackerTestClient for sails_rs::client::Actor { @@ -19,12 +27,12 @@ impl MsgTrackerTestClient fn dynamic_counter( &self, ) -> sails_rs::client::Service { - self.service(stringify!(DynamicCounter)) + self.service(MsgTrackerTestClientProgram::ROUTE_ID_DYNAMIC_COUNTER) } fn fixed_counter( &self, ) -> sails_rs::client::Service { - self.service(stringify!(FixedCounter)) + self.service(MsgTrackerTestClientProgram::ROUTE_ID_FIXED_COUNTER) } } pub trait MsgTrackerTestClientCtors { @@ -33,6 +41,7 @@ pub trait MsgTrackerTestClientCtors { #[allow(clippy::wrong_self_convention)] fn new(self) -> sails_rs::client::PendingCtor; } + impl MsgTrackerTestClientCtors for sails_rs::client::Deployment { @@ -44,11 +53,40 @@ impl MsgTrackerTestClientCtors pub mod io { use super::*; - sails_rs::io_struct_impl!(New () -> ()); + sails_rs::io_struct_impl!(New () -> (), 0); } pub mod dynamic_counter { use super::*; + + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub enum CounterError { + OperationNotFound, + AlreadyCompleted, + } + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub enum OpStatus { + Pending, + Completed, + } + /// Pagination parameters for listing tracked messages. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Pagination { + /// The number of items to skip. + pub offset: u32, + /// The maximum number of items to return. + pub limit: u32, + } + pub trait DynamicCounter { type Env: sails_rs::client::GearEnv; fn clear_dynamic(&mut self) -> sails_rs::client::PendingCall; @@ -56,14 +94,6 @@ pub mod dynamic_counter { &mut self, id: MessageId, ) -> sails_rs::client::PendingCall; - fn request_increment( - &mut self, - ) -> sails_rs::client::PendingCall; - fn update_dynamic( - &mut self, - id: MessageId, - status: OpStatus, - ) -> sails_rs::client::PendingCall; fn get_status( &self, id: MessageId, @@ -73,8 +103,23 @@ pub mod dynamic_counter { query: Option, ) -> sails_rs::client::PendingCall; fn get_val(&self) -> sails_rs::client::PendingCall; + fn request_increment( + &mut self, + ) -> sails_rs::client::PendingCall; + fn update_dynamic( + &mut self, + id: MessageId, + status: OpStatus, + ) -> sails_rs::client::PendingCall; } + pub struct DynamicCounterImpl; + + impl sails_rs::client::Identifiable for DynamicCounterImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([212, 115, 76, 100, 213, 87, 180, 25]); + } + impl DynamicCounter for sails_rs::client::Service { @@ -88,18 +133,6 @@ pub mod dynamic_counter { ) -> sails_rs::client::PendingCall { self.pending_call((id,)) } - fn request_increment( - &mut self, - ) -> sails_rs::client::PendingCall { - self.pending_call(()) - } - fn update_dynamic( - &mut self, - id: MessageId, - status: OpStatus, - ) -> sails_rs::client::PendingCall { - self.pending_call((id, status)) - } fn get_status( &self, id: MessageId, @@ -115,22 +148,63 @@ pub mod dynamic_counter { fn get_val(&self) -> sails_rs::client::PendingCall { self.pending_call(()) } + fn request_increment( + &mut self, + ) -> sails_rs::client::PendingCall { + self.pending_call(()) + } + fn update_dynamic( + &mut self, + id: MessageId, + status: OpStatus, + ) -> sails_rs::client::PendingCall { + self.pending_call((id, status)) + } } pub mod io { use super::*; - sails_rs::io_struct_impl!(ClearDynamic () -> ()); - sails_rs::io_struct_impl!(ConfirmIncrement (id: MessageId) -> ()); - sails_rs::io_struct_impl!(RequestIncrement () -> MessageId); - sails_rs::io_struct_impl!(UpdateDynamic (id: MessageId, status: super::OpStatus) -> bool); - sails_rs::io_struct_impl!(GetStatus (id: MessageId) -> Option); - sails_rs::io_struct_impl!(GetStatuses (query: Option) -> Vec<(MessageId,super::OpStatus,)>); - sails_rs::io_struct_impl!(GetVal () -> u32); + sails_rs::io_struct_impl!(ClearDynamic () -> (), 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(ConfirmIncrement (id: MessageId) -> () | super::CounterError, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetStatus (id: MessageId) -> super::Option, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetStatuses (query: super::Option) -> Vec<(MessageId, super::OpStatus, )>, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetVal () -> u32, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RequestIncrement () -> MessageId, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(UpdateDynamic (id: MessageId, status: super::OpStatus) -> bool, 6, ::INTERFACE_ID); } } pub mod fixed_counter { use super::*; + + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub enum OpStatus { + Pending, + Completed, + } + /// Pagination parameters for listing tracked messages. + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub struct Pagination { + /// The number of items to skip. + pub offset: u32, + /// The maximum number of items to return. + pub limit: u32, + } + #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] + #[codec(crate = sails_rs::scale_codec)] + #[scale_info(crate = sails_rs::scale_info)] + #[reflect_hash(crate = sails_rs)] + pub enum TrackerError { + /// Indicates that the fixed storage capacity has been exceeded. + CapacityExceeded, + } + pub trait FixedCounter { type Env: sails_rs::client::GearEnv; fn clear_fixed(&mut self) -> sails_rs::client::PendingCall; @@ -138,6 +212,15 @@ pub mod fixed_counter { &mut self, id: MessageId, ) -> sails_rs::client::PendingCall; + fn get_status( + &self, + id: MessageId, + ) -> sails_rs::client::PendingCall; + fn get_statuses( + &self, + query: Option, + ) -> sails_rs::client::PendingCall; + fn get_val(&self) -> sails_rs::client::PendingCall; fn remove_fixed( &mut self, id: MessageId, @@ -150,17 +233,15 @@ pub mod fixed_counter { id: MessageId, status: OpStatus, ) -> sails_rs::client::PendingCall; - fn get_status( - &self, - id: MessageId, - ) -> sails_rs::client::PendingCall; - fn get_statuses( - &self, - query: Option, - ) -> sails_rs::client::PendingCall; - fn get_val(&self) -> sails_rs::client::PendingCall; } + pub struct FixedCounterImpl; + + impl sails_rs::client::Identifiable for FixedCounterImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([203, 111, 47, 177, 123, 98, 150, 230]); + } + impl FixedCounter for sails_rs::client::Service { type Env = E; fn clear_fixed(&mut self) -> sails_rs::client::PendingCall { @@ -172,6 +253,21 @@ pub mod fixed_counter { ) -> sails_rs::client::PendingCall { self.pending_call((id,)) } + fn get_status( + &self, + id: MessageId, + ) -> sails_rs::client::PendingCall { + self.pending_call((id,)) + } + fn get_statuses( + &self, + query: Option, + ) -> sails_rs::client::PendingCall { + self.pending_call((query,)) + } + fn get_val(&self) -> sails_rs::client::PendingCall { + self.pending_call(()) + } fn remove_fixed( &mut self, id: MessageId, @@ -190,49 +286,17 @@ pub mod fixed_counter { ) -> sails_rs::client::PendingCall { self.pending_call((id, status)) } - fn get_status( - &self, - id: MessageId, - ) -> sails_rs::client::PendingCall { - self.pending_call((id,)) - } - fn get_statuses( - &self, - query: Option, - ) -> sails_rs::client::PendingCall { - self.pending_call((query,)) - } - fn get_val(&self) -> sails_rs::client::PendingCall { - self.pending_call(()) - } } pub mod io { use super::*; - sails_rs::io_struct_impl!(ClearFixed () -> ()); - sails_rs::io_struct_impl!(ConfirmIncrement (id: MessageId) -> bool); - sails_rs::io_struct_impl!(RemoveFixed (id: MessageId) -> Option); - sails_rs::io_struct_impl!(RequestIncrement () -> MessageId); - sails_rs::io_struct_impl!(UpdateFixed (id: MessageId, status: super::OpStatus) -> bool); - sails_rs::io_struct_impl!(GetStatus (id: MessageId) -> Option); - sails_rs::io_struct_impl!(GetStatuses (query: Option) -> Vec<(MessageId,super::OpStatus,)>); - sails_rs::io_struct_impl!(GetVal () -> u32); + sails_rs::io_struct_impl!(ClearFixed () -> (), 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(ConfirmIncrement (id: MessageId) -> bool, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetStatus (id: MessageId) -> super::Option, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetStatuses (query: super::Option) -> Vec<(MessageId, super::OpStatus, )>, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetVal () -> u32, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RemoveFixed (id: MessageId) -> super::Option, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(RequestIncrement () -> MessageId | super::TrackerError, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(UpdateFixed (id: MessageId, status: super::OpStatus) -> bool, 7, ::INTERFACE_ID); } } -#[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo)] -#[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] -pub enum OpStatus { - Pending, - Completed, -} -/// Pagination parameters for listing tracked messages. -#[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo)] -#[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] -pub struct Pagination { - /// The number of items to skip. - pub offset: u32, - /// The maximum number of items to return. - pub limit: u32, -} From 869006243135ded64c97dfc905a291775474f590 Mon Sep 17 00:00:00 2001 From: Mansur Azatbek Date: Wed, 8 Apr 2026 14:24:11 +0500 Subject: [PATCH 3/5] chore: update sails-rs to =1.0.0-beta.3 --- Cargo.lock | 112 ++++++++++++------ Cargo.toml | 5 +- .../awesome-sails/access-control/src/lib.rs | 7 +- crates/awesome-sails/msg-tracker/src/lib.rs | 1 - .../awesome-sails/msg-tracker/src/storage.rs | 2 - crates/awesome-sails/vft-admin/src/lib.rs | 3 +- crates/awesome-sails/vft-extension/src/lib.rs | 1 - crates/awesome-sails/vft-metadata/src/lib.rs | 1 - .../vft-native-exchange-admin/src/lib.rs | 3 +- crates/awesome-sails/vft/src/lib.rs | 1 - .../awesome-sails/vft/utils/src/allowances.rs | 1 - .../awesome-sails/vft/utils/src/balances.rs | 1 - crates/awesome-sails/vft/utils/src/lib.rs | 2 - tests/access-control-test/app/tests/gtest.rs | 6 +- .../client/access_control_test_client.idl | 2 +- .../client/src/access_control_test_client.rs | 15 ++- .../app/tests/common/mod.rs | 2 +- tests/awesome-sails-test/app/tests/gtest.rs | 10 +- .../client/awesome_sails_test_client.idl | 2 +- .../client/src/awesome_sails_test_client.rs | 54 +++++++-- tests/msg-tracker-test/app/src/lib.rs | 2 - .../msg-tracker-test/app/tests/common/mod.rs | 2 +- tests/msg-tracker-test/app/tests/gtest.rs | 2 +- .../client/msg_tracker_test_client.idl | 2 +- .../client/src/msg_tracker_test_client.rs | 14 +-- utils/Cargo.toml | 2 +- utils/src/error.rs | 7 +- utils/src/map.rs | 3 +- utils/src/math.rs | 19 ++- utils/src/pause.rs | 5 +- 30 files changed, 171 insertions(+), 118 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b9d5c86..86664e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,7 +540,7 @@ dependencies = [ "primitive-types 0.12.2", "proptest", "sails-reflect-hash", - "scale-info", + "sails-type-registry", "thiserror 2.0.17", ] @@ -2135,7 +2135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81a088fd6fda5f53ff0c17fc7551ce8bd0ead14ba742228443c8196296a7369b" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -4091,7 +4091,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -4383,11 +4383,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.10+spec-1.1.0", ] [[package]] @@ -4922,8 +4922,10 @@ dependencies = [ ] [[package]] -name = "sails-client-gen" -version = "1.0.0-beta.2" +name = "sails-client-gen-v2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f587aedf29cac0c52e0899a067e2ee427414b1938b097ac6cdb317fe8648b99f" dependencies = [ "anyhow", "convert_case 0.11.0", @@ -4935,7 +4937,9 @@ dependencies = [ [[package]] name = "sails-idl-embed" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9089d57344b21ff35ba65da739bb1b65de42714ed1635e14cd14cbee98a55283" dependencies = [ "flate2", "tempfile", @@ -4946,31 +4950,37 @@ dependencies = [ [[package]] name = "sails-idl-gen" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc7554e5f81b8e153f70b17992faa055b000da43069b88ede943725e8de3af0" dependencies = [ "askama", "convert_case 0.11.0", "gprimitives", "quote", "sails-idl-meta", - "scale-info", + "sails-type-registry", "syn 2.0.117", "thiserror 2.0.17", ] [[package]] name = "sails-idl-meta" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8809a36d36dbe616b970c56a6064f10f9e69cff4306bbaa102bb828d8bf28b63" dependencies = [ "askama", "keccak-const", "parity-scale-codec", - "scale-info", + "sails-type-registry", ] [[package]] name = "sails-idl-parser-v2" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd15edebbd1cf6c621d34ec24b79b9d93cb5cd4fb37645b3a5b697a91379a975" dependencies = [ "paste", "pest", @@ -4982,7 +4992,9 @@ dependencies = [ [[package]] name = "sails-macros" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "318693d72143e65964a95f8c503083f3e23cb5e2869e3e5e47aa885f1af9500d" dependencies = [ "proc-macro-error", "sails-macros-core", @@ -4990,7 +5002,9 @@ dependencies = [ [[package]] name = "sails-macros-core" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7fc06d034aa78c171f882b203c6d3732b0cccd08e7c1a3bb8e674e27d75d33" dependencies = [ "convert_case 0.11.0", "parity-scale-codec", @@ -5002,7 +5016,9 @@ dependencies = [ [[package]] name = "sails-reflect-hash" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dff0f90793e06898b6a31ad4f3ace4dcf9e66cdf44cd7a3c605d72cf55e4116" dependencies = [ "gprimitives", "keccak-const", @@ -5011,9 +5027,11 @@ dependencies = [ [[package]] name = "sails-reflect-hash-derive" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32dc237766ceee804d95d6057d5cbdab1616f5a9b6fe20e5a5e33b258950b061" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5021,7 +5039,9 @@ dependencies = [ [[package]] name = "sails-rs" -version = "1.0.0-beta.2" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "880a26b5571cde5571cafde37800894f2f8adfc5923315d1657b0cfe104e8f2e" dependencies = [ "convert_case 0.11.0", "futures", @@ -5041,18 +5061,40 @@ dependencies = [ "parity-scale-codec", "paste", "pin-project-lite", - "sails-client-gen", + "sails-client-gen-v2", "sails-idl-embed", "sails-idl-gen", "sails-idl-meta", "sails-macros", "sails-reflect-hash", - "scale-info", + "sails-type-registry", "spin 0.10.0", "thiserror 2.0.17", "tokio-stream", ] +[[package]] +name = "sails-type-registry" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843edb1b6a19b2456eaaab53ddff6b5d4ad6aa9c47b4043ada0454d6e1464e5d" +dependencies = [ + "gprimitives", + "sails-type-registry-derive", +] + +[[package]] +name = "sails-type-registry-derive" +version = "1.0.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2348e472249c2464644c2739cc3008f433126188cf694e6213642a06a58a512" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "same-file" version = "1.0.6" @@ -5121,7 +5163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17020f2d59baabf2ddcdc20a4e567f8210baf089b8a8d4785f5fd5e716f92038" dependencies = [ "darling 0.20.11", - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5147,7 +5189,7 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5505,7 +5547,7 @@ dependencies = [ "Inflector", "blake2", "expander", - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5798,7 +5840,7 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6345,9 +6387,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -6379,23 +6421,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b" dependencies = [ "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 0.7.14", + "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 0.7.14", + "winnow 1.0.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8db5d08..9d81e17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,8 +40,9 @@ members = [ [workspace.dependencies] -sails-rs = { path = "../sails/rs", default-features = false, features = ["gstd"] } -sails-reflect-hash = { path = "../sails/rs/reflect-hash", default-features = false } +sails-rs = { version = "=1.0.0-beta.3", default-features = false, features = ["gstd"] } +sails-reflect-hash = { version = "=1.0.0-beta.3", default-features = false } +sails-type-registry = { version = "=1.0.0-beta.3", default-features = false, features = ["derive"] } awesome-sails-utils = { path = "utils", version = "0.2.0", default-features = false } gtest = "=1.10.0" gprimitives = { version = "1.10.0", default-features = false } diff --git a/crates/awesome-sails/access-control/src/lib.rs b/crates/awesome-sails/access-control/src/lib.rs index 55a2fa9..b139890 100644 --- a/crates/awesome-sails/access-control/src/lib.rs +++ b/crates/awesome-sails/access-control/src/lib.rs @@ -73,7 +73,6 @@ pub struct RoleData { /// Pagination parameters for listing roles or members. #[derive(Encode, Decode, TypeInfo, Debug, Clone, Copy, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. @@ -581,7 +580,6 @@ impl<'a, S: InfallibleStorageMut> AccessControl<'a, S> { #[event] #[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when `target_account` is granted `role_id`. @@ -610,16 +608,14 @@ pub mod error { use crate::RoleId; pub use awesome_sails_utils::error::{BadOrigin, EmitError, Error}; use sails_rs::{ - ActorId, ReflectHash, + ActorId, ReflectHash, TypeInfo, scale_codec::{Decode, Encode}, - scale_info::TypeInfo, }; /// Error indicating access was denied due to missing role permissions. #[derive(Clone, Debug, Decode, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] #[error("Access denied: account {account_id:?} does not have role {role_id:?}")] - #[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub struct AccessDenied { pub account_id: ActorId, @@ -630,7 +626,6 @@ pub mod error { #[derive(Clone, Debug, Decode, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] #[error("Not account owner: account {account_id:?}, message source {message_source:?}")] - #[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub struct NotAccountOwner { pub account_id: ActorId, diff --git a/crates/awesome-sails/msg-tracker/src/lib.rs b/crates/awesome-sails/msg-tracker/src/lib.rs index 1982be8..af613dd 100644 --- a/crates/awesome-sails/msg-tracker/src/lib.rs +++ b/crates/awesome-sails/msg-tracker/src/lib.rs @@ -77,7 +77,6 @@ where /// Pagination parameters for listing tracked messages. #[derive(Clone, Copy, Debug, Decode, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. diff --git a/crates/awesome-sails/msg-tracker/src/storage.rs b/crates/awesome-sails/msg-tracker/src/storage.rs index 1fb149e..8750a19 100644 --- a/crates/awesome-sails/msg-tracker/src/storage.rs +++ b/crates/awesome-sails/msg-tracker/src/storage.rs @@ -66,7 +66,6 @@ impl MessageStorage for BTreeMap { /// Storage implementation using fixed-size arrays. #[derive(Debug, Encode, Decode, TypeInfo)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] pub struct FixedStorage { /// Array of tracked message identifiers. pub ids: [MessageId; N], @@ -179,7 +178,6 @@ pub mod error { #[derive(Debug, Decode, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum TrackerError { /// Indicates that the fixed storage capacity has been exceeded. diff --git a/crates/awesome-sails/vft-admin/src/lib.rs b/crates/awesome-sails/vft-admin/src/lib.rs index 7363f1d..905a0a3 100644 --- a/crates/awesome-sails/vft-admin/src/lib.rs +++ b/crates/awesome-sails/vft-admin/src/lib.rs @@ -360,11 +360,10 @@ impl< } } -/// Events emitted by the VFT admin service. +/// Events emitted by the VFT Admin service. #[event] #[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when a burn operation occurs. diff --git a/crates/awesome-sails/vft-extension/src/lib.rs b/crates/awesome-sails/vft-extension/src/lib.rs index e8a71af..b2d5af2 100644 --- a/crates/awesome-sails/vft-extension/src/lib.rs +++ b/crates/awesome-sails/vft-extension/src/lib.rs @@ -334,5 +334,4 @@ impl, B: StorageMut> VftExtens )] #[codec(crate = sails_rs::scale_codec)] #[error("allowance is not expired")] -#[scale_info(crate = sails_rs::scale_info)] pub struct AllowanceNotExpiredError; diff --git a/crates/awesome-sails/vft-metadata/src/lib.rs b/crates/awesome-sails/vft-metadata/src/lib.rs index 6794528..81136e7 100644 --- a/crates/awesome-sails/vft-metadata/src/lib.rs +++ b/crates/awesome-sails/vft-metadata/src/lib.rs @@ -95,7 +95,6 @@ impl> VftMetadata { /// Represents the metadata of a VFT: name, symbol, and decimals. #[derive(Debug, Clone, Encode, Decode, TypeInfo, ReflectHash, PartialEq, Eq, PartialOrd, Ord)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub struct Metadata { name: String, diff --git a/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs b/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs index 16b825d..d2043bb 100644 --- a/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs +++ b/crates/awesome-sails/vft-native-exchange-admin/src/lib.rs @@ -120,11 +120,10 @@ where } } -/// Events emitted by the VFT native exchange admin service. +/// Events emitted by the VFT Native Exchange Admin service. #[event] #[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when re-minting tokens after a failed transfer fails. diff --git a/crates/awesome-sails/vft/src/lib.rs b/crates/awesome-sails/vft/src/lib.rs index e5fae8a..3f4832e 100644 --- a/crates/awesome-sails/vft/src/lib.rs +++ b/crates/awesome-sails/vft/src/lib.rs @@ -241,7 +241,6 @@ impl, B: StorageMut> Vft<'_, A #[event] #[derive(Clone, Debug, PartialEq, Encode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum Event { /// Emitted when an approval is granted or updated. diff --git a/crates/awesome-sails/vft/utils/src/allowances.rs b/crates/awesome-sails/vft/utils/src/allowances.rs index 13ed2a6..bfa2cec 100644 --- a/crates/awesome-sails/vft/utils/src/allowances.rs +++ b/crates/awesome-sails/vft/utils/src/allowances.rs @@ -202,7 +202,6 @@ impl Allowances { Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, TypeInfo, thiserror::Error, )] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] pub enum AllowancesError { #[error("insufficient allowance")] Insufficient(#[from] UnderflowError), diff --git a/crates/awesome-sails/vft/utils/src/balances.rs b/crates/awesome-sails/vft/utils/src/balances.rs index afa4a37..48089b4 100644 --- a/crates/awesome-sails/vft/utils/src/balances.rs +++ b/crates/awesome-sails/vft/utils/src/balances.rs @@ -365,7 +365,6 @@ where #[derive(Clone, Debug, Decode, Encode, TypeInfo, thiserror::Error)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] pub enum BalancesError { #[error("insufficient balance")] Insufficient(#[from] UnderflowError), diff --git a/crates/awesome-sails/vft/utils/src/lib.rs b/crates/awesome-sails/vft/utils/src/lib.rs index 53a5a42..f84cd17 100644 --- a/crates/awesome-sails/vft/utils/src/lib.rs +++ b/crates/awesome-sails/vft/utils/src/lib.rs @@ -35,7 +35,6 @@ pub use balances::{Balances, BalancesError}; // 72 bits = 9 bytes #[derive(Clone, Copy, Debug, Default, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, TypeInfo)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] pub struct Allowance(LeBytes<9>); impl_math_wrapper!(Allowance, LeBytes<9>); @@ -52,7 +51,6 @@ impl From for Allowance { // 80 bits = 10 bytes #[derive(Clone, Copy, Debug, Default, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, TypeInfo)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] pub struct Balance(LeBytes<10>); impl_math_wrapper!(Balance, LeBytes<10>); diff --git a/tests/access-control-test/app/tests/gtest.rs b/tests/access-control-test/app/tests/gtest.rs index 2f6377f..cf3a66e 100644 --- a/tests/access-control-test/app/tests/gtest.rs +++ b/tests/access-control-test/app/tests/gtest.rs @@ -20,9 +20,7 @@ mod common; use access_control_test_client::{ AccessControlTestClient, - access_control::{ - AccessControl, Error as AccessControlError, Pagination, events::AccessControlEvents, - }, + access_control::{AccessControl, Pagination, events::AccessControlEvents}, }; use awesome_sails::access_control::{DEFAULT_ADMIN_ROLE, RoleId}; use awesome_sails_utils::assert_ok; @@ -613,7 +611,7 @@ async fn enumeration_pagination_logic() { // 1. Grant 10 roles to Bob (Total roles: 1 admin + 10 new = 11) let roles: Vec = (1..=10).map(|i| [i as u8; 32]).collect(); - access_control_service + let _ = access_control_service .grant_roles_batch(roles, BOB) .with_actor_id(ALICE) .await diff --git a/tests/access-control-test/client/access_control_test_client.idl b/tests/access-control-test/client/access_control_test_client.idl index 82ec8f1..693c0ed 100644 --- a/tests/access-control-test/client/access_control_test_client.idl +++ b/tests/access-control-test/client/access_control_test_client.idl @@ -1,5 +1,5 @@ -!@sails: 1.0.0-beta.2 +!@sails: 1.0.0-beta.3 service AccessControl@0x26662bab76fe8d0f { events { diff --git a/tests/access-control-test/client/src/access_control_test_client.rs b/tests/access-control-test/client/src/access_control_test_client.rs index 94b7e30..5e0f551 100644 --- a/tests/access-control-test/client/src/access_control_test_client.rs +++ b/tests/access-control-test/client/src/access_control_test_client.rs @@ -1,4 +1,4 @@ -// Code generated by sails-client-gen. DO NOT EDIT. +// Code generated by sails-client-gen-v2. DO NOT EDIT. #[allow(unused_imports)] use sails_rs::{client::*, collections::*, prelude::*}; pub struct AccessControlTestClientProgram; @@ -59,13 +59,13 @@ pub mod access_control { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); /// Pagination parameters for listing roles or members. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. @@ -399,7 +399,14 @@ pub mod access_control { } } - impl sails_rs::client::Event for AccessControlEvents {} + impl sails_rs::client::Event for AccessControlEvents { + fn decode_event( + route: &sails_rs::client::RouteIdx, + payload: impl AsRef<[u8]>, + ) -> Result { + sails_rs::client::decode_event_v2::(route.0, payload) + } + } impl sails_rs::client::Identifiable for AccessControlEvents { const INTERFACE_ID: sails_rs::InterfaceId = diff --git a/tests/awesome-sails-test/app/tests/common/mod.rs b/tests/awesome-sails-test/app/tests/common/mod.rs index b0ac2b4..c2aa4f7 100644 --- a/tests/awesome-sails-test/app/tests/common/mod.rs +++ b/tests/awesome-sails-test/app/tests/common/mod.rs @@ -25,7 +25,7 @@ use awesome_sails_test_client::{ }; use sails_rs::{ ActorId, U256, - client::{Actor, GearEnv, GtestEnv, GtestError}, + client::{Actor, GearEnv, GtestEnv}, gtest::System, prelude::*, }; diff --git a/tests/awesome-sails-test/app/tests/gtest.rs b/tests/awesome-sails-test/app/tests/gtest.rs index 13bf43b..caaaba3 100644 --- a/tests/awesome-sails-test/app/tests/gtest.rs +++ b/tests/awesome-sails-test/app/tests/gtest.rs @@ -326,7 +326,10 @@ async fn transfer() { ); let res = vft_service.balance_of(ALICE).await; - assert_ok!(res.unwrap(), U256::exp10(MAGIC - 1) + U256::exp10(MAGIC - 2)); + assert_ok!( + res.unwrap(), + U256::exp10(MAGIC - 1) + U256::exp10(MAGIC - 2) + ); let res = vft_service.balance_of(BOB).await; assert_ok!( @@ -727,7 +730,10 @@ async fn transfer_from() { assert_ok!(res.unwrap(), None); let res = vft_service.balance_of(CHARLIE).await; - assert_ok!(res.unwrap(), U256::exp10(MAGIC - 1) - U256::exp10(MAGIC - 2)); + assert_ok!( + res.unwrap(), + U256::exp10(MAGIC - 1) - U256::exp10(MAGIC - 2) + ); let res = vft_service.balance_of(DAVE).await; assert_ok!(res.unwrap(), U256::exp10(MAGIC) + U256::exp10(MAGIC - 2)); diff --git a/tests/awesome-sails-test/client/awesome_sails_test_client.idl b/tests/awesome-sails-test/client/awesome_sails_test_client.idl index 3256f28..2f49866 100644 --- a/tests/awesome-sails-test/client/awesome_sails_test_client.idl +++ b/tests/awesome-sails-test/client/awesome_sails_test_client.idl @@ -1,5 +1,5 @@ -!@sails: 1.0.0-beta.2 +!@sails: 1.0.0-beta.3 service Test@0x279e57efe3de371d { functions { diff --git a/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs b/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs index a098291..482c63a 100644 --- a/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs +++ b/tests/awesome-sails-test/client/src/awesome_sails_test_client.rs @@ -1,4 +1,4 @@ -// Code generated by sails-client-gen. DO NOT EDIT. +// Code generated by sails-client-gen-v2. DO NOT EDIT. #[allow(unused_imports)] use sails_rs::{client::*, collections::*, prelude::*}; pub struct AwesomeSailsTestClientProgram; @@ -107,7 +107,7 @@ pub mod test { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); @@ -154,13 +154,13 @@ pub mod access_control { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); /// Pagination parameters for listing roles or members. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. @@ -494,7 +494,14 @@ pub mod access_control { } } - impl sails_rs::client::Event for AccessControlEvents {} + impl sails_rs::client::Event for AccessControlEvents { + fn decode_event( + route: &sails_rs::client::RouteIdx, + payload: impl AsRef<[u8]>, + ) -> Result { + sails_rs::client::decode_event_v2::(route.0, payload) + } + } impl sails_rs::client::Identifiable for AccessControlEvents { const INTERFACE_ID: sails_rs::InterfaceId = @@ -515,7 +522,7 @@ pub mod vft { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); @@ -703,7 +710,14 @@ pub mod vft { } } - impl sails_rs::client::Event for VftEvents {} + impl sails_rs::client::Event for VftEvents { + fn decode_event( + route: &sails_rs::client::RouteIdx, + payload: impl AsRef<[u8]>, + ) -> Result { + sails_rs::client::decode_event_v2::(route.0, payload) + } + } impl sails_rs::client::Identifiable for VftEvents { const INTERFACE_ID: sails_rs::InterfaceId = @@ -724,7 +738,7 @@ pub mod vft_admin { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); @@ -949,7 +963,14 @@ pub mod vft_admin { } } - impl sails_rs::client::Event for VftAdminEvents {} + impl sails_rs::client::Event for VftAdminEvents { + fn decode_event( + route: &sails_rs::client::RouteIdx, + payload: impl AsRef<[u8]>, + ) -> Result { + sails_rs::client::decode_event_v2::(route.0, payload) + } + } impl sails_rs::client::Identifiable for VftAdminEvents { const INTERFACE_ID: sails_rs::InterfaceId = @@ -970,7 +991,7 @@ pub mod vft_extension { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); @@ -1260,7 +1281,7 @@ pub mod vft_native_exchange { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); @@ -1328,7 +1349,7 @@ pub mod vft_native_exchange_admin { /// This struct wraps a string message providing details about the error. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Error(pub String); @@ -1399,7 +1420,14 @@ pub mod vft_native_exchange_admin { } } - impl sails_rs::client::Event for VftNativeExchangeAdminEvents {} + impl sails_rs::client::Event for VftNativeExchangeAdminEvents { + fn decode_event( + route: &sails_rs::client::RouteIdx, + payload: impl AsRef<[u8]>, + ) -> Result { + sails_rs::client::decode_event_v2::(route.0, payload) + } + } impl sails_rs::client::Identifiable for VftNativeExchangeAdminEvents { const INTERFACE_ID: sails_rs::InterfaceId = diff --git a/tests/msg-tracker-test/app/src/lib.rs b/tests/msg-tracker-test/app/src/lib.rs index f6cd0df..a49f2a3 100644 --- a/tests/msg-tracker-test/app/src/lib.rs +++ b/tests/msg-tracker-test/app/src/lib.rs @@ -9,7 +9,6 @@ use sails_rs::{cell::RefCell, prelude::*}; #[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum OpStatus { Pending, @@ -18,7 +17,6 @@ pub enum OpStatus { #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] -#[scale_info(crate = sails_rs::scale_info)] #[reflect_hash(crate = sails_rs)] pub enum CounterError { OperationNotFound, diff --git a/tests/msg-tracker-test/app/tests/common/mod.rs b/tests/msg-tracker-test/app/tests/common/mod.rs index 4aa30ac..f3d3c68 100644 --- a/tests/msg-tracker-test/app/tests/common/mod.rs +++ b/tests/msg-tracker-test/app/tests/common/mod.rs @@ -1,7 +1,7 @@ use msg_tracker_test_client::{MsgTrackerTestClientCtors, MsgTrackerTestClientProgram}; use sails_rs::{ ActorId, - client::{Actor, GearEnv, GtestEnv, GtestError}, + client::{Actor, GearEnv, GtestEnv}, gtest::System, prelude::*, }; diff --git a/tests/msg-tracker-test/app/tests/gtest.rs b/tests/msg-tracker-test/app/tests/gtest.rs index 46e8539..0aefff0 100644 --- a/tests/msg-tracker-test/app/tests/gtest.rs +++ b/tests/msg-tracker-test/app/tests/gtest.rs @@ -14,7 +14,7 @@ async fn test_fixed_counter_overflow_panic() { let mut fixed_counter = program.fixed_counter(); for _ in 0..5 { - fixed_counter.request_increment().await.unwrap(); + let _ = fixed_counter.request_increment().await.unwrap(); } let res = fixed_counter.request_increment().await.unwrap(); diff --git a/tests/msg-tracker-test/client/msg_tracker_test_client.idl b/tests/msg-tracker-test/client/msg_tracker_test_client.idl index 9f919b4..67f4c5c 100644 --- a/tests/msg-tracker-test/client/msg_tracker_test_client.idl +++ b/tests/msg-tracker-test/client/msg_tracker_test_client.idl @@ -1,5 +1,5 @@ -!@sails: 1.0.0-beta.2 +!@sails: 1.0.0-beta.3 service DynamicCounter@0xd4734c64d557b419 { functions { diff --git a/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs b/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs index a8ef84f..34ba933 100644 --- a/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs +++ b/tests/msg-tracker-test/client/src/msg_tracker_test_client.rs @@ -1,4 +1,4 @@ -// Code generated by sails-client-gen. DO NOT EDIT. +// Code generated by sails-client-gen-v2. DO NOT EDIT. #[allow(unused_imports)] use sails_rs::{client::*, collections::*, prelude::*}; pub struct MsgTrackerTestClientProgram; @@ -61,7 +61,7 @@ pub mod dynamic_counter { #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub enum CounterError { OperationNotFound, @@ -69,7 +69,7 @@ pub mod dynamic_counter { } #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub enum OpStatus { Pending, @@ -78,7 +78,7 @@ pub mod dynamic_counter { /// Pagination parameters for listing tracked messages. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. @@ -179,7 +179,7 @@ pub mod fixed_counter { #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub enum OpStatus { Pending, @@ -188,7 +188,7 @@ pub mod fixed_counter { /// Pagination parameters for listing tracked messages. #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub struct Pagination { /// The number of items to skip. @@ -198,7 +198,7 @@ pub mod fixed_counter { } #[derive(PartialEq, Clone, Debug, Encode, Decode, TypeInfo, ReflectHash)] #[codec(crate = sails_rs::scale_codec)] - #[scale_info(crate = sails_rs::scale_info)] + #[type_info(crate = sails_rs::type_info)] #[reflect_hash(crate = sails_rs)] pub enum TrackerError { /// Indicates that the fixed storage capacity has been exceeded. diff --git a/utils/Cargo.toml b/utils/Cargo.toml index 3d9b56f..5fd39cc 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -13,7 +13,7 @@ description = "Utilities for Awesome Sails services" primitive-types = { workspace = true } gprimitives = { workspace = true, optional = true } parity-scale-codec = { workspace = true, features = ["derive"] } -scale-info = { workspace = true, features = ["derive"] } +sails-type-registry = { workspace = true } derive_more = { workspace = true, features = [ "display", "from", diff --git a/utils/src/error.rs b/utils/src/error.rs index 9c22de7..401d3b4 100644 --- a/utils/src/error.rs +++ b/utils/src/error.rs @@ -26,14 +26,13 @@ use alloc::string::{String, ToString}; use core::fmt; use parity_scale_codec::{Decode, Encode}; use sails_reflect_hash::ReflectHash; -use scale_info::TypeInfo; +use sails_type_registry::TypeInfo; /// Represents a generic error type within the `awesome-sails` ecosystem. /// /// This struct wraps a string message providing details about the error. #[derive(Clone, Decode, Encode, TypeInfo, ReflectHash, derive_more::Display)] #[codec(crate = parity_scale_codec)] -#[scale_info(crate = scale_info)] #[display("{}", _0)] pub struct Error(String); @@ -70,7 +69,6 @@ impl From for Error { #[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("incorrect input argument")] -#[scale_info(crate = scale_info)] pub struct BadInput; /// Indicates an operation was attempted by an incorrect origin. @@ -80,7 +78,6 @@ pub struct BadInput; #[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("incorrect message origin")] -#[scale_info(crate = scale_info)] pub struct BadOrigin; /// Indicates an incorrect value was attached to the message. @@ -89,7 +86,6 @@ pub struct BadOrigin; #[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("incorrect message value")] -#[scale_info(crate = scale_info)] pub struct BadValue; /// Indicates a failure occurred while attempting to emit an event. @@ -98,5 +94,4 @@ pub struct BadValue; #[derive(Clone, Debug, Decode, Default, Encode, TypeInfo, ReflectHash, thiserror::Error)] #[codec(crate = parity_scale_codec)] #[error("emit event error")] -#[scale_info(crate = scale_info)] pub struct EmitError; diff --git a/utils/src/map.rs b/utils/src/map.rs index 68a23ae..3d793ef 100644 --- a/utils/src/map.rs +++ b/utils/src/map.rs @@ -28,7 +28,7 @@ use alloc::vec::Vec; use core::{hash::Hash, mem}; use hashbrown::HashMap; use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; +use sails_type_registry::TypeInfo; /// A sharded hash map implementation. /// @@ -349,7 +349,6 @@ impl ShardIdx { Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Decode, Encode, TypeInfo, thiserror::Error, )] #[codec(crate = parity_scale_codec)] -#[scale_info(crate = scale_info)] pub enum ShardedMapError { /// Indicates that the operation failed because the map (or shard) is at full capacity. #[error("capacity overflow")] diff --git a/utils/src/math.rs b/utils/src/math.rs index e72c7e8..e7affa0 100644 --- a/utils/src/math.rs +++ b/utils/src/math.rs @@ -27,7 +27,7 @@ use bnum::BUintD8; use core::cmp::Ordering; use derive_more::Deref; use parity_scale_codec::{Decode, Encode}; -use scale_info::{Path, Type, TypeInfo, build::Fields}; +use sails_type_registry::{TypeInfo, ty::Type}; pub use primitive_types::{H160, H256, U256}; @@ -385,11 +385,15 @@ impl Decode for LeBytes { impl TypeInfo for LeBytes { type Identity = Self; - fn type_info() -> Type { + fn type_info(registry: &mut sails_type_registry::Registry) -> Type { + let u8_slice = registry.register_type::<[u8]>(); Type::builder() - .path(Path::new("LeBytes", module_path!())) - .type_params(vec::Vec::new()) - .composite(Fields::unnamed().field(|f| f.ty::<[u8]>())) + .module_path(module_path!()) + .name("LeBytes") + .composite() + .unnamed() + .ty(u8_slice) + .build() } } @@ -491,7 +495,6 @@ impl TryFrom> for U256 { /// A wrapper type that ensures the contained value is not zero. #[derive(Clone, Copy, Debug, PartialEq, Eq, Decode, Encode, TypeInfo, Hash, Deref)] #[codec(crate = parity_scale_codec)] -#[scale_info(crate = scale_info)] pub struct NonZero(T); impl NonZero { @@ -620,7 +623,6 @@ impl From>> for LeBytes { )] #[codec(crate = parity_scale_codec)] #[error(transparent)] -#[scale_info(crate = scale_info)] pub enum MathError { /// Indicates an arithmetic overflow occurred. Overflow(#[from] OverflowError), @@ -646,7 +648,6 @@ pub enum MathError { )] #[codec(crate = parity_scale_codec)] #[error("mathematical overflow")] -#[scale_info(crate = scale_info)] pub struct OverflowError; /// Error type indicating a mathematical underflow. @@ -665,7 +666,6 @@ pub struct OverflowError; )] #[codec(crate = parity_scale_codec)] #[error("mathematical underflow")] -#[scale_info(crate = scale_info)] pub struct UnderflowError; /// Error type indicating a zero value where it is not allowed. @@ -684,5 +684,4 @@ pub struct UnderflowError; )] #[codec(crate = parity_scale_codec)] #[error("zero error")] -#[scale_info(crate = scale_info)] pub struct ZeroError; diff --git a/utils/src/pause.rs b/utils/src/pause.rs index c206268..7bb1bbf 100644 --- a/utils/src/pause.rs +++ b/utils/src/pause.rs @@ -30,7 +30,7 @@ use core::{ ops::{Deref, DerefMut}, }; use parity_scale_codec::{Decode, Encode}; -use scale_info::TypeInfo; +use sails_type_registry::TypeInfo; /// A wrapper around a storage type that adds pause functionality. /// @@ -194,7 +194,6 @@ impl Pause { Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Decode, Encode, TypeInfo, thiserror::Error, )] #[codec(crate = parity_scale_codec)] -#[scale_info(crate = scale_info)] pub enum PausableError { /// The operation failed because the storage is paused. #[error("storage is paused")] @@ -210,7 +209,6 @@ pub enum PausableError { )] #[codec(crate = parity_scale_codec)] #[error("enabled pause error")] -#[scale_info(crate = scale_info)] pub struct PausedError; /// Error indicating that an operation required the system to be paused, but it was unpaused. @@ -219,5 +217,4 @@ pub struct PausedError; )] #[codec(crate = parity_scale_codec)] #[error("disabled pause error")] -#[scale_info(crate = scale_info)] pub struct UnpausedError; From fac5b46b7f1248466a0366d7bc497eec2678c4b8 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Fri, 17 Apr 2026 00:24:39 +0400 Subject: [PATCH 4/5] feat(benchmarks): add GasTrace qualitative gas profiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a reply-linked message tree reconstructor for `gtest::BlockRunResult` to complement the existing quantitative `MeasureGas` / `BenchStorage` tools. - `GasTrace` builder merges logs and `gas_burned` across one or more blocks, groups replies by `reply_to`, surfaces events as top-level, and renders a gas-annotated ASCII tree via `Display`. - `MethodRegistry` resolves `(InterfaceId, entry_id)` pairs back to `Service::method` names using `ServiceMeta::{INTERFACE_ID, METHODS}`. - gtest's `block.log()` surfaces replies and events but not the originating request — `GasTrace` synthesizes a root for any `gas_burned` entry whose id isn't represented in the tree and nests its reply under it, so gas attribution isn't silently dropped. - Gated on the existing `gtest` feature (no impact when disabled). - 16 unit tests (registry, tree reconstruction, display formatting, edge cases) + 1 end-to-end integration demo against `access-control-test-app`. Limitation documented in `GasTrace` rustdoc + README: cross-program sub-calls (A -> B -> C) appear as separate roots because `gtest::CoreLog` doesn't expose parent-message causality. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 2 + benchmarks/Cargo.toml | 9 +- benchmarks/README.md | 47 +- benchmarks/src/lib.rs | 5 + benchmarks/src/trace.rs | 987 +++++++++++++++++++++++ benchmarks/tests/cases/gas_trace_demo.rs | 61 ++ benchmarks/tests/cases/mod.rs | 1 + 7 files changed, 1109 insertions(+), 3 deletions(-) create mode 100644 benchmarks/src/trace.rs create mode 100644 benchmarks/tests/cases/gas_trace_demo.rs diff --git a/Cargo.lock b/Cargo.lock index 86664e3..257cf64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -483,6 +483,8 @@ dependencies = [ "anyhow", "clap", "cli-table", + "gear-core", + "gear-core-errors", "gprimitives", "gtest", "sails-rs", diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index 2eb4dda..889b034 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -17,6 +17,11 @@ serde_json = "1.0" gtest = { workspace = true, optional = true } gprimitives = { workspace = true } +# Gas trace tree (gtest only) +sails-rs = { workspace = true, features = ["gtest"], optional = true } +gear-core = { version = "=1.10.0", optional = true } +gear-core-errors = { version = "=1.10.0", optional = true } + # Optional reporting cli-table = { version = "0.5", optional = true } clap = { version = "4.0", features = ["derive"], optional = true } @@ -27,8 +32,8 @@ default = ["gtest"] "ascii-table" = ["dep:cli-table"] # Enables the bench-analyzer CLI binary cli = ["ascii-table", "dep:clap"] -# Enables gtest-based benchmarking -gtest = ["dep:gtest"] +# Enables gtest-based benchmarking (includes GasTrace profiler) +gtest = ["dep:gtest", "dep:sails-rs", "dep:gear-core", "dep:gear-core-errors"] [[bin]] name = "bench-analyzer" diff --git a/benchmarks/README.md b/benchmarks/README.md index 758828a..82b760a 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -71,7 +71,52 @@ async fn bench_my_service() { } ``` -### 2. Analysis +### 2. Gas Trace Tree (Qualitative Profiling) + +While `MeasureGas` answers "how much gas?", `GasTrace` answers "**where** did the +gas go?" — it reconstructs a reply-linked message tree from one or more +`BlockRunResult`s, decorates each node with its gas cost, and resolves +`(interface_id, entry_id)` pairs back to `Service::method` names via a +`MethodRegistry`. Output is a human-readable ASCII tree. + +```rust +use awesome_sails_benchmarks::{GasTrace, MethodRegistry}; + +let block = system.run_next_block(); + +let registry = MethodRegistry::new() + .register_service::("MyService"); + +let tree = GasTrace::new(&block) + .with_registry(®istry) + .with_actor_name(user_id, "alice") + .build(); + +println!("{}", tree); +``` + +Example output: + +``` +[0x1001..abcd] alice -> MyProgram::MyService::increment 12,400 gas + `-- [0x1002..9876] [reply] Ok - gas +[event] MyProgram +Total: 12,400 gas | 3 messages | depth 1 +``` + +**Limitations (gtest runtime constraints, not tool design):** + +- The tree is built from `reply_to` linkage. Cross-program sub-calls (A → B → C) + appear as separate roots because `gtest::CoreLog` doesn't expose + parent-message causality. +- `block.log()` surfaces replies and events but not the originating request. + `GasTrace` synthesizes a root for any `gas_burned` entry whose id isn't + represented elsewhere in the tree and nests its reply under it — this + recovers gas attribution but the synthesized root has no payload, so its + method is shown as `[raw]` unless a future gtest revision exposes the full + request. + +### 3. Analysis The `bench-analyzer` utility compares generated results with a baseline. diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs index c11e21d..6bc279f 100644 --- a/benchmarks/src/lib.rs +++ b/benchmarks/src/lib.rs @@ -29,6 +29,11 @@ use gprimitives::MessageId; #[cfg(feature = "gtest")] use gtest::System; +#[cfg(feature = "gtest")] +mod trace; +#[cfg(feature = "gtest")] +pub use trace::{GasTrace, GasTraceNode, GasTraceTree, MethodInfo, MethodRegistry}; + #[cfg(feature = "gtest")] const RELEASE_MODE_ERROR: &str = "Benchmarks MUST be run in --release mode to get accurate gas measurements."; diff --git a/benchmarks/src/trace.rs b/benchmarks/src/trace.rs new file mode 100644 index 0000000..0be1e4d --- /dev/null +++ b/benchmarks/src/trace.rs @@ -0,0 +1,987 @@ +// This file is part of Gear. + +// Copyright (C) 2026 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Qualitative gas profiling — reply-linked message tree with gas annotations +//! and resolved method names, built from `gtest::BlockRunResult` logs. + +use gprimitives::{ActorId, MessageId}; +use gtest::BlockRunResult; +use sails_rs::meta::{InterfaceId, SailsMessageHeader, ServiceMeta}; +use std::collections::{BTreeMap, HashMap}; + +/// Registry mapping (InterfaceId, entry_id) pairs to human-readable method names. +/// +/// Built via the builder pattern using `register_service` for each service. +#[derive(Default, Clone)] +pub struct MethodRegistry { + /// Key: (interface_id as u64, entry_id) -> "ServiceName::method_name" + methods: HashMap<(u64, u16), String>, + /// Key: interface_id as u64 -> service name + services: HashMap, +} + +impl MethodRegistry { + pub fn new() -> Self { + Self::default() + } + + /// Register all methods from a `ServiceMeta` implementation. + /// + /// `service_name` is the human-readable name (e.g., `"Counter"`). + pub fn register_service(mut self, service_name: &str) -> Self { + let iid = S::INTERFACE_ID.as_u64(); + self.services.insert(iid, service_name.to_string()); + for method in S::METHODS { + self.methods.insert( + (iid, method.entry_id), + format!("{}::{}", service_name, method.name), + ); + } + self + } + + /// Resolve interface_id + entry_id to a human-readable method name. + pub fn resolve(&self, interface_id: InterfaceId, entry_id: u16) -> Option<&str> { + self.methods + .get(&(interface_id.as_u64(), entry_id)) + .map(|s| s.as_str()) + } + + /// Resolve just the service name from an interface_id. + pub fn resolve_service(&self, interface_id: InterfaceId) -> Option<&str> { + self.services + .get(&interface_id.as_u64()) + .map(|s| s.as_str()) + } +} + +/// Decoded method identification from a message payload. +#[derive(Debug, Clone)] +pub enum MethodInfo { + /// Successfully decoded a Sails header. + Sails { + interface_id: InterfaceId, + entry_id: u16, + resolved_name: Option, + }, + /// Not a Sails message (no GM magic, too short, etc.) + Raw, +} + +/// A single node in the gas trace tree. +#[derive(Debug, Clone)] +pub struct GasTraceNode { + pub message_id: MessageId, + pub source: ActorId, + pub destination: ActorId, + pub gas: Option, + pub method: MethodInfo, + pub is_reply: bool, + pub reply_code: Option, + pub is_event: bool, + pub children: Vec, +} + +/// A fully reconstructed message trace tree with gas annotations. +#[derive(Debug, Clone)] +pub struct GasTraceTree { + pub roots: Vec, + pub total_gas: u64, + pub total_messages: usize, + pub max_depth: usize, + actor_names: HashMap, +} + +/// Builder for constructing gas trace trees from `BlockRunResult`s. +/// +/// # Limitations +/// +/// The tree is built from `reply_to` links, which connect replies to their +/// originating request. For single-program tests (user -> program -> reply), +/// this produces a complete call tree. For cross-program calls (A -> B -> C), +/// outbound sub-calls appear as separate root messages because gtest's `CoreLog` +/// does not expose parent-message causality. +pub struct GasTrace<'a> { + blocks: Vec<&'a BlockRunResult>, + registry: Option<&'a MethodRegistry>, + actor_names: HashMap, +} + +impl<'a> GasTrace<'a> { + /// Create a new trace from a single block result. + pub fn new(block: &'a BlockRunResult) -> Self { + Self { + blocks: vec![block], + registry: None, + actor_names: HashMap::new(), + } + } + + /// Create a trace spanning multiple block results. + pub fn from_blocks(blocks: impl IntoIterator) -> Self { + Self { + blocks: blocks.into_iter().collect(), + registry: None, + actor_names: HashMap::new(), + } + } + + /// Attach a method registry for human-readable method names. + pub fn with_registry(mut self, registry: &'a MethodRegistry) -> Self { + self.registry = Some(registry); + self + } + + /// Register a human-readable name for an actor. + pub fn with_actor_name(mut self, actor_id: ActorId, name: impl Into) -> Self { + self.actor_names.insert(actor_id, name.into()); + self + } + + /// Build the trace tree. + pub fn build(&self) -> GasTraceTree { + // 1. Merge all logs and gas_burned from all blocks + let mut all_logs = Vec::new(); + let mut all_gas: BTreeMap = BTreeMap::new(); + + for block in &self.blocks { + for entry in block.log().iter() { + all_logs.push(entry); + } + for (&msg_id, &gas) in &block.gas_burned { + all_gas.insert(msg_id, gas); + } + } + + // 2. Index replies: reply_to -> list of log entries that are replies to it + let mut replies_by_parent: HashMap> = HashMap::new(); + let mut roots = Vec::new(); + let mut events = Vec::new(); + + for entry in &all_logs { + if entry.destination() == ActorId::zero() { + events.push(*entry); + } else if let Some(parent_id) = entry.reply_to() { + replies_by_parent.entry(parent_id).or_default().push(entry); + } else { + roots.push(*entry); + } + } + + // 3. Build tree recursively + let mut root_nodes: Vec = roots + .iter() + .map(|entry| self.build_node(entry, &replies_by_parent, &all_gas)) + .collect(); + + // 4. Collect orphaned replies (reply_to target not in any block) + let root_ids: std::collections::HashSet = roots.iter().map(|e| e.id()).collect(); + for (parent_id, replies) in &replies_by_parent { + if !root_ids.contains(parent_id) + && !replies_by_parent + .values() + .any(|v| v.iter().any(|r| r.id() == *parent_id)) + { + // This parent was never seen as a root or as another reply's child + for reply in replies { + root_nodes.push(GasTraceNode { + message_id: reply.id(), + source: reply.source(), + destination: reply.destination(), + gas: all_gas.get(&reply.id()).copied(), + method: self.decode_method(reply.payload()), + is_reply: true, + reply_code: reply.reply_code(), + is_event: false, + children: Vec::new(), + }); + } + } + } + + // 5. Events as top-level entries + for entry in &events { + root_nodes.push(GasTraceNode { + message_id: entry.id(), + source: entry.source(), + destination: entry.destination(), + gas: all_gas.get(&entry.id()).copied(), + method: self.decode_method(entry.payload()), + is_reply: false, + reply_code: None, + is_event: true, + children: Vec::new(), + }); + } + + // 6. Surface gas_burned entries whose id is not represented anywhere + // in the tree. In gtest, the originating request often doesn't + // appear in `block.log()` (only its reply and events do) but its + // gas IS recorded under its id in `gas_burned`. Without this step + // those gas numbers would be silently dropped. + let mut seen_ids: std::collections::HashSet = std::collections::HashSet::new(); + for node in &root_nodes { + collect_ids(node, &mut seen_ids); + } + for (&msg_id, &gas) in &all_gas { + if !seen_ids.contains(&msg_id) { + // Try to attach this gas to a reply whose reply_to points here + // (so the synthetic request becomes the reply's parent). + if let Some(replies) = replies_by_parent.get(&msg_id) { + let children: Vec = replies + .iter() + .map(|reply| self.build_node(reply, &replies_by_parent, &all_gas)) + .collect(); + // Drop the now-adopted orphans from root_nodes + let reply_ids: std::collections::HashSet = + replies.iter().map(|r| r.id()).collect(); + root_nodes.retain(|n| !reply_ids.contains(&n.message_id)); + root_nodes.push(GasTraceNode { + message_id: msg_id, + source: ActorId::zero(), + destination: ActorId::zero(), + gas: Some(gas), + method: MethodInfo::Raw, + is_reply: false, + reply_code: None, + is_event: false, + children, + }); + } else { + // No linkage — surface as a bare node. + root_nodes.push(GasTraceNode { + message_id: msg_id, + source: ActorId::zero(), + destination: ActorId::zero(), + gas: Some(gas), + method: MethodInfo::Raw, + is_reply: false, + reply_code: None, + is_event: false, + children: Vec::new(), + }); + } + } + } + + // 7. Compute totals from tree nodes, not raw maps + let total_gas = sum_tree_gas(&root_nodes); + let total_messages = all_logs.len(); + let max_depth = root_nodes + .iter() + .map(|n| self.tree_depth(n)) + .max() + .unwrap_or(0); + + GasTraceTree { + roots: root_nodes, + total_gas, + total_messages, + max_depth, + actor_names: self.actor_names.clone(), + } + } + + /// Build and print to stdout. + pub fn print(&self) { + std::println!("{}", self.build()); + } + + /// Build and return as a formatted string. + pub fn to_string_pretty(&self) -> String { + format!("{}", self.build()) + } + + fn build_node( + &self, + entry: >est::CoreLog, + replies_by_parent: &HashMap>, + all_gas: &BTreeMap, + ) -> GasTraceNode { + let children = replies_by_parent + .get(&entry.id()) + .map(|replies| { + replies + .iter() + .map(|reply| self.build_node(reply, replies_by_parent, all_gas)) + .collect() + }) + .unwrap_or_default(); + + GasTraceNode { + message_id: entry.id(), + source: entry.source(), + destination: entry.destination(), + gas: all_gas.get(&entry.id()).copied(), + method: self.decode_method(entry.payload()), + is_reply: entry.reply_to().is_some(), + reply_code: entry.reply_code(), + is_event: false, + children, + } + } + + fn decode_method(&self, payload: &[u8]) -> MethodInfo { + match SailsMessageHeader::try_from_bytes(payload) { + Ok(header) => { + let interface_id = header.interface_id(); + let entry_id = header.entry_id(); + let resolved_name = self + .registry + .and_then(|r| r.resolve(interface_id, entry_id)) + .map(|s| s.to_string()); + MethodInfo::Sails { + interface_id, + entry_id, + resolved_name, + } + } + Err(_) => MethodInfo::Raw, + } + } + + #[allow(dead_code)] + fn sum_gas(&self, nodes: &[GasTraceNode]) -> u64 { + nodes + .iter() + .map(|n| n.gas.unwrap_or(0) + self.sum_gas(&n.children)) + .sum() + } + + fn tree_depth(&self, node: &GasTraceNode) -> usize { + if node.children.is_empty() { + 0 + } else { + 1 + node + .children + .iter() + .map(|c| self.tree_depth(c)) + .max() + .unwrap_or(0) + } + } +} + +fn sum_tree_gas(nodes: &[GasTraceNode]) -> u64 { + nodes + .iter() + .map(|n| n.gas.unwrap_or(0) + sum_tree_gas(&n.children)) + .sum() +} + +fn collect_ids(node: &GasTraceNode, seen: &mut std::collections::HashSet) { + seen.insert(node.message_id); + for child in &node.children { + collect_ids(child, seen); + } +} + +impl std::fmt::Display for GasTraceTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.roots.is_empty() { + return write!(f, "(empty trace)"); + } + + for root in &self.roots { + format_node(f, root, "", true, &self.actor_names)?; + } + write!( + f, + "Total: {} gas | {} messages | depth {}", + format_gas(self.total_gas), + self.total_messages, + self.max_depth, + ) + } +} + +fn format_node( + f: &mut std::fmt::Formatter<'_>, + node: &GasTraceNode, + prefix: &str, + is_last: bool, + actor_names: &HashMap, +) -> std::fmt::Result { + let connector = if prefix.is_empty() { + "" + } else if is_last { + "`-- " + } else { + "+-- " + }; + + // Message ID (first 4 bytes) + let msg_id_hex = format!("{:.4}", node.message_id); + + if node.is_event { + let source_name = format_actor(node.source, actor_names); + writeln!(f, "{prefix}{connector}[event] {source_name}")?; + } else if node.is_reply { + let code_str = match node.reply_code { + Some(gear_core_errors::ReplyCode::Success(_)) => "Ok", + Some(gear_core_errors::ReplyCode::Error(ref reason)) => match reason { + gear_core_errors::ErrorReplyReason::Execution(e) => match e { + gear_core_errors::SimpleExecutionError::RanOutOfGas => "Err(RanOutOfGas)", + _ => "Err(Execution)", + }, + _ => "Err", + }, + Some(gear_core_errors::ReplyCode::Unsupported) => "Unsupported", + None => "?", + }; + let gas_str = format_gas_opt(node.gas); + writeln!( + f, + "{prefix}{connector}[{msg_id_hex}] [reply] {code_str} {gas_str}" + )?; + } else { + let source_name = format_actor(node.source, actor_names); + let dest_name = format_actor(node.destination, actor_names); + let method_str = format_method(&node.method); + let gas_str = format_gas_opt(node.gas); + writeln!( + f, + "{prefix}{connector}[{msg_id_hex}] {source_name} -> {dest_name}::{method_str} {gas_str}" + )?; + } + + // Children + let child_prefix = if prefix.is_empty() { + " ".to_string() + } else if is_last { + format!("{prefix} ") + } else { + format!("{prefix}| ") + }; + + let child_count = node.children.len(); + for (i, child) in node.children.iter().enumerate() { + let child_is_last = i == child_count - 1; + format_node(f, child, &child_prefix, child_is_last, actor_names)?; + } + + Ok(()) +} + +fn format_actor(actor_id: ActorId, actor_names: &HashMap) -> String { + if let Some(name) = actor_names.get(&actor_id) { + name.clone() + } else { + format!("{:.4}", actor_id) + } +} + +fn format_method(method: &MethodInfo) -> String { + match method { + MethodInfo::Sails { + resolved_name: Some(name), + .. + } => name.clone(), + MethodInfo::Sails { + interface_id, + entry_id, + .. + } => format!("{interface_id}#{entry_id}"), + MethodInfo::Raw => "[raw]".to_string(), + } +} + +fn format_gas(gas: u64) -> String { + if gas == 0 { + return "0".to_string(); + } + + let s = gas.to_string(); + let mut result = String::with_capacity(s.len() + s.len() / 3); + for (i, c) in s.chars().enumerate() { + if i > 0 && (s.len() - i).is_multiple_of(3) { + result.push(','); + } + result.push(c); + } + result +} + +fn format_gas_opt(gas: Option) -> String { + match gas { + Some(g) => format!("{} gas", format_gas(g)), + None => "- gas".to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use gear_core::message::{MessageDetails, ReplyDetails, StoredMessage}; + use gprimitives::{ActorId, MessageId}; + + fn make_log( + id: MessageId, + source: ActorId, + destination: ActorId, + payload: Vec, + reply_to: Option, + reply_code: Option, + ) -> gtest::CoreLog { + let details = match (reply_to, reply_code) { + (Some(reply_id), Some(code)) => { + Some(MessageDetails::Reply(ReplyDetails::new(reply_id, code))) + } + _ => None, + }; + let stored = StoredMessage::new( + id, + source, + destination, + payload.try_into().expect("payload too large"), + 0, + details, + ); + gtest::CoreLog::from(stored) + } + + fn make_sails_payload(interface_id: InterfaceId, entry_id: u16, route_id: u8) -> Vec { + SailsMessageHeader::v1(interface_id, entry_id, route_id).to_bytes() + } + + fn make_block(logs: Vec, gas_burned: Vec<(MessageId, u64)>) -> BlockRunResult { + BlockRunResult { + log: logs, + gas_burned: gas_burned.into_iter().collect(), + ..Default::default() + } + } + + // --- MethodRegistry tests --- + + #[test] + fn registry_register_and_resolve() { + // We can't easily create a real ServiceMeta impl in tests, + // so test the HashMap-based resolve directly. + let mut registry = MethodRegistry::new(); + let iid = InterfaceId::from_u64(42); + registry + .methods + .insert((iid.as_u64(), 0), "Counter::increment".to_string()); + registry + .services + .insert(iid.as_u64(), "Counter".to_string()); + + assert_eq!(registry.resolve(iid, 0), Some("Counter::increment")); + assert_eq!(registry.resolve_service(iid), Some("Counter")); + } + + #[test] + fn registry_resolve_unknown() { + let registry = MethodRegistry::new(); + let iid = InterfaceId::from_u64(999); + assert_eq!(registry.resolve(iid, 0), None); + assert_eq!(registry.resolve_service(iid), None); + } + + #[test] + fn registry_empty_service() { + // Empty registry still works, just resolves nothing + let registry = MethodRegistry::new(); + assert_eq!(registry.resolve(InterfaceId::zero(), 0), None); + } + + #[test] + fn registry_duplicate_registration() { + let mut registry = MethodRegistry::new(); + let iid = InterfaceId::from_u64(1); + registry + .methods + .insert((iid.as_u64(), 0), "First::method".to_string()); + registry + .methods + .insert((iid.as_u64(), 0), "Second::method".to_string()); + // Last write wins + assert_eq!(registry.resolve(iid, 0), Some("Second::method")); + } + + // --- Tree reconstruction tests --- + + #[test] + fn single_block_root_and_reply() { + let user = ActorId::from(1u64); + let program = ActorId::from(2u64); + let msg_id = MessageId::from(100u64); + let reply_id = MessageId::from(101u64); + + let payload = make_sails_payload(InterfaceId::from_u64(42), 0, 1); + let root_log = make_log(msg_id, user, program, payload, None, None); + let reply_log = make_log( + reply_id, + program, + user, + vec![], + Some(msg_id), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + let block = make_block(vec![root_log, reply_log], vec![(msg_id, 5000)]); + let tree = GasTrace::new(&block).build(); + + assert_eq!(tree.roots.len(), 1); + assert_eq!(tree.roots[0].message_id, msg_id); + assert_eq!(tree.roots[0].children.len(), 1); + assert!(tree.roots[0].children[0].is_reply); + assert_eq!(tree.total_messages, 2); + assert_eq!(tree.total_gas, 5000); + } + + #[test] + fn multi_block_merge() { + let user = ActorId::from(1u64); + let program = ActorId::from(2u64); + let msg_id = MessageId::from(200u64); + let reply_id = MessageId::from(201u64); + + let payload = make_sails_payload(InterfaceId::from_u64(1), 0, 1); + let root_log = make_log(msg_id, user, program, payload, None, None); + let reply_log = make_log( + reply_id, + program, + user, + vec![], + Some(msg_id), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + let block1 = make_block(vec![root_log], vec![(msg_id, 3000)]); + let block2 = make_block(vec![reply_log], vec![(reply_id, 200)]); + + let tree = GasTrace::from_blocks([&block1, &block2]).build(); + + assert_eq!(tree.roots.len(), 1); + assert_eq!(tree.roots[0].children.len(), 1); + assert_eq!(tree.total_gas, 3200); + assert_eq!(tree.total_messages, 2); + } + + #[test] + fn non_sails_payload_falls_back_to_raw() { + let msg_id = MessageId::from(300u64); + let log = make_log( + msg_id, + ActorId::from(1u64), + ActorId::from(2u64), + vec![0x00, 0x01, 0x02], + None, + None, + ); + + let block = make_block(vec![log], vec![(msg_id, 1000)]); + let tree = GasTrace::new(&block).build(); + + assert!(matches!(tree.roots[0].method, MethodInfo::Raw)); + } + + #[test] + fn events_as_top_level() { + let program = ActorId::from(2u64); + let msg_id = MessageId::from(400u64); + let event_id = MessageId::from(401u64); + + let root_log = make_log( + msg_id, + ActorId::from(1u64), + program, + make_sails_payload(InterfaceId::from_u64(1), 0, 1), + None, + None, + ); + let event_log = make_log( + event_id, + program, + ActorId::zero(), + vec![0xAB, 0xCD], + None, + None, + ); + + let block = make_block(vec![root_log, event_log], vec![(msg_id, 2000)]); + let tree = GasTrace::new(&block).build(); + + // Root message + event = 2 top-level entries + assert_eq!(tree.roots.len(), 2); + assert!(!tree.roots[0].is_event); + assert!(tree.roots[1].is_event); + } + + #[test] + fn orphaned_reply_as_top_level() { + let reply_id = MessageId::from(501u64); + let missing_parent = MessageId::from(500u64); + + let reply_log = make_log( + reply_id, + ActorId::from(2u64), + ActorId::from(1u64), + vec![], + Some(missing_parent), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + let block = make_block(vec![reply_log], vec![(reply_id, 100)]); + let tree = GasTrace::new(&block).build(); + + // Orphaned reply: its parent (msg 500) isn't in the block. + // The reply is surfaced as a top-level node so it's not silently lost. + assert_eq!(tree.roots.len(), 1); + assert!(tree.roots[0].is_reply); + assert_eq!(tree.total_messages, 1); + assert_eq!(tree.total_gas, 100); + } + + #[test] + fn empty_block_produces_empty_tree() { + let block = make_block(vec![], vec![]); + let tree = GasTrace::new(&block).build(); + + assert!(tree.roots.is_empty()); + assert_eq!(tree.total_gas, 0); + assert_eq!(tree.total_messages, 0); + assert_eq!(tree.max_depth, 0); + } + + // --- Display tests --- + + #[test] + fn display_single_root_with_reply() { + let user = ActorId::from(1u64); + let program = ActorId::from(2u64); + let msg_id = MessageId::from(100u64); + let reply_id = MessageId::from(101u64); + + let payload = make_sails_payload(InterfaceId::from_u64(42), 0, 1); + let root_log = make_log(msg_id, user, program, payload, None, None); + let reply_log = make_log( + reply_id, + program, + user, + vec![], + Some(msg_id), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + let block = make_block( + vec![root_log, reply_log], + vec![(msg_id, 5000), (reply_id, 200)], + ); + + let output = GasTrace::new(&block) + .with_actor_name(user, "alice") + .with_actor_name(program, "MyProgram") + .to_string_pretty(); + + assert!(output.contains("alice")); + assert!(output.contains("MyProgram")); + assert!(output.contains("[reply] Ok")); + assert!(output.contains("5,200 gas")); + } + + #[test] + fn display_deep_nesting() { + let a = ActorId::from(1u64); + let b = ActorId::from(2u64); + let id1 = MessageId::from(1u64); + let id2 = MessageId::from(2u64); + let id3 = MessageId::from(3u64); + + let payload = make_sails_payload(InterfaceId::from_u64(1), 0, 1); + let log1 = make_log(id1, a, b, payload.clone(), None, None); + let log2 = make_log( + id2, + b, + a, + vec![], + Some(id1), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + // A third reply nested under id2 (unusual but tests depth) + let log3 = make_log( + id3, + a, + b, + vec![], + Some(id2), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + let block = make_block( + vec![log1, log2, log3], + vec![(id1, 1000), (id2, 500), (id3, 100)], + ); + let tree = GasTrace::new(&block).build(); + + assert_eq!(tree.max_depth, 2); + let output = format!("{tree}"); + assert!(output.contains("depth 2")); + } + + #[test] + fn display_missing_gas() { + let msg_id = MessageId::from(600u64); + let log = make_log( + msg_id, + ActorId::from(1u64), + ActorId::from(2u64), + make_sails_payload(InterfaceId::from_u64(1), 0, 1), + None, + None, + ); + + // No gas_burned entry for this message + let block = make_block(vec![log], vec![]); + let output = GasTrace::new(&block).to_string_pretty(); + + assert!(output.contains("- gas")); + } + + #[test] + fn display_empty_tree() { + let block = make_block(vec![], vec![]); + let output = GasTrace::new(&block).to_string_pretty(); + + assert_eq!(output, "(empty trace)"); + } + + // --- Formatting helpers --- + + #[test] + fn demo_output() { + let alice = ActorId::from(1u64); + let program_a = ActorId::from(2u64); + let program_b = ActorId::from(3u64); + + let iid_counter = InterfaceId::from_u64(0xAAAA_BBBB_CCCC_0001); + let iid_storage = InterfaceId::from_u64(0xDDDD_EEEE_FFFF_0002); + + // alice -> ProgramA::Counter::increment (root call) + let msg1 = MessageId::from(0x1001u64); + let log1 = make_log( + msg1, + alice, + program_a, + make_sails_payload(iid_counter, 0, 1), + None, + None, + ); + + // ProgramA -> ProgramB::Storage::write (cross-program, separate root) + let msg2 = MessageId::from(0x2002u64); + let log2 = make_log( + msg2, + program_a, + program_b, + make_sails_payload(iid_storage, 1, 1), + None, + None, + ); + + // ProgramB replies to ProgramA + let reply2 = MessageId::from(0x2003u64); + let log_reply2 = make_log( + reply2, + program_b, + program_a, + vec![], + Some(msg2), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + // ProgramA replies to alice + let reply1 = MessageId::from(0x1002u64); + let log_reply1 = make_log( + reply1, + program_a, + alice, + vec![], + Some(msg1), + Some(gear_core_errors::ReplyCode::Success( + gear_core_errors::SuccessReplyReason::Manual, + )), + ); + + // Event emitted by ProgramA + let event_id = MessageId::from(0x3001u64); + let log_event = make_log( + event_id, + program_a, + ActorId::zero(), + vec![0xAB, 0xCD], + None, + None, + ); + + let block = make_block( + vec![log1, log2, log_reply2, log_reply1, log_event], + vec![(msg1, 12_400), (msg2, 8_200), (reply2, 200), (reply1, 150)], + ); + + let mut registry = MethodRegistry::new(); + registry + .methods + .insert((iid_counter.as_u64(), 0), "Counter::increment".to_string()); + registry + .services + .insert(iid_counter.as_u64(), "Counter".to_string()); + registry + .methods + .insert((iid_storage.as_u64(), 1), "Storage::write".to_string()); + registry + .services + .insert(iid_storage.as_u64(), "Storage".to_string()); + + let output = GasTrace::new(&block) + .with_registry(®istry) + .with_actor_name(alice, "alice") + .with_actor_name(program_a, "ProgramA") + .with_actor_name(program_b, "ProgramB") + .to_string_pretty(); + + assert!(output.contains("Counter::increment")); + assert!(output.contains("Storage::write")); + assert!(output.contains("alice")); + } + + #[test] + fn format_gas_with_separators() { + assert_eq!(format_gas(0), "0"); + assert_eq!(format_gas(999), "999"); + assert_eq!(format_gas(1000), "1,000"); + assert_eq!(format_gas(1_000_000), "1,000,000"); + assert_eq!(format_gas(12_345), "12,345"); + } +} diff --git a/benchmarks/tests/cases/gas_trace_demo.rs b/benchmarks/tests/cases/gas_trace_demo.rs new file mode 100644 index 0000000..4fde29a --- /dev/null +++ b/benchmarks/tests/cases/gas_trace_demo.rs @@ -0,0 +1,61 @@ +// This file is part of Gear. + +// Copyright (C) 2026 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! End-to-end demo of the `GasTrace` qualitative profiler. +//! +//! Fires a real message at the access-control test program, captures the +//! resulting `BlockRunResult`, and renders it as a gas-annotated tree. + +use crate::common; +use access_control_test_client::{AccessControlTestClient, access_control::AccessControl}; +use awesome_sails_benchmarks::GasTrace; +use sails_rs::{ActorId, prelude::*}; + +#[tokio::test] +#[ignore = "bench"] +async fn gas_trace_demo_access_control() { + let wasm_name = "access_control_test_app.opt.wasm"; + let wasm_path = common::get_wasm_path(wasm_name); + + let env = common::create_env(); + let program = common::deploy_program(&env, wasm_path).await; + let mut service = program.access_control(); + let system = env.system(); + + // Send one grant_role call and capture the resulting block. + let role_id = [1u8; 32]; + let member: ActorId = 1234u64.into(); + service + .grant_role(role_id, member) + .send_one_way() + .expect("send failed"); + let block = system.run_next_block(); + + // Build a trace tree. No registry — raw entries print as + // `{interface_id}#{entry_id}`, which is still useful for validation. + let tree = GasTrace::new(&block) + .with_actor_name(common::ALICE.into(), "alice") + .build(); + + assert!(!tree.roots.is_empty(), "expected at least one root message"); + assert!(tree.total_gas > 0, "expected gas > 0"); + assert_eq!(tree.total_messages, block.log().len()); + + // Human-readable output (visible with `cargo test -- --nocapture`). + std::println!("{}", tree); +} diff --git a/benchmarks/tests/cases/mod.rs b/benchmarks/tests/cases/mod.rs index 80d4e74..1e436ba 100644 --- a/benchmarks/tests/cases/mod.rs +++ b/benchmarks/tests/cases/mod.rs @@ -17,3 +17,4 @@ // along with this program. If not, see . mod access_control; +mod gas_trace_demo; From 1362d37f67f87953b7fadf1f3a941033636b0398 Mon Sep 17 00:00:00 2001 From: vobradovich Date: Wed, 17 Jun 2026 15:08:39 +0200 Subject: [PATCH 5/5] merge --- tests/access-control-test/app/tests/common/mod.rs | 2 +- tests/awesome-sails-test/app/tests/common/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/access-control-test/app/tests/common/mod.rs b/tests/access-control-test/app/tests/common/mod.rs index 9996590..c6a8825 100644 --- a/tests/access-control-test/app/tests/common/mod.rs +++ b/tests/access-control-test/app/tests/common/mod.rs @@ -81,7 +81,7 @@ pub async fn deploy_program() -> ( .deploy::(code_id, b"salt".to_vec()) .new() .await - .unwrap(); + .expect("failed to deploy program"); let program_id = program.id(); diff --git a/tests/awesome-sails-test/app/tests/common/mod.rs b/tests/awesome-sails-test/app/tests/common/mod.rs index c2aa4f7..fb46a18 100644 --- a/tests/awesome-sails-test/app/tests/common/mod.rs +++ b/tests/awesome-sails-test/app/tests/common/mod.rs @@ -91,7 +91,7 @@ pub async fn deploy_with_data( .deploy::(code_id, b"salt".to_vec()) .new() .await - .unwrap(); + .expect("failed to deploy program"); let program_id = program.id();