From 28d971b11666aa16f3d4fd040f85473ce0d6a401 Mon Sep 17 00:00:00 2001 From: Daan van der Plas <93204684+Daanvdplas@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:00:18 +0200 Subject: [PATCH] feat: api fungibles pallet (#113) Co-authored-by: Frank Bell --- Cargo.lock | 231 ++--- Cargo.toml | 2 + node/src/command.rs | 13 +- pallets/api/Cargo.toml | 55 ++ pallets/api/src/fungibles/benchmarking.rs | 106 +++ pallets/api/src/fungibles/mod.rs | 240 +++++ pallets/api/src/fungibles/tests.rs | 147 +++ pallets/api/src/fungibles/weights.rs | 94 ++ pallets/api/src/lib.rs | 5 + pallets/api/src/mock.rs | 123 +++ pop-api/Cargo.toml | 10 +- pop-api/examples/fungibles/Cargo.toml | 2 +- pop-api/integration-tests/Cargo.toml | 2 +- .../contracts/fungibles/Cargo.toml | 21 + .../contracts/fungibles/lib.rs | 185 ++++ .../integration-tests/src/local_fungibles.rs | 135 +-- pop-api/src/lib.rs | 14 +- pop-api/src/primitives.rs | 3 - pop-api/src/v0/assets/fungibles.rs | 284 +++--- pop-api/src/v0/assets/mod.rs | 235 +---- pop-api/src/v0/balances.rs | 88 -- pop-api/src/v0/cross_chain/coretime.rs | 11 - pop-api/src/v0/cross_chain/mod.rs | 107 --- pop-api/src/v0/mod.rs | 6 - pop-api/src/v0/nfts.rs | 883 ------------------ primitives/Cargo.toml | 14 +- primitives/README.md | 1 + primitives/src/cross_chain.rs | 19 - primitives/src/lib.rs | 28 +- primitives/src/storage_keys.rs | 56 -- runtime/devnet/Cargo.toml | 8 +- runtime/devnet/src/config/api.rs | 34 + runtime/devnet/src/config/assets.rs | 9 +- runtime/devnet/src/config/mod.rs | 1 + runtime/devnet/src/extensions/mod.rs | 824 ++-------------- .../src/extensions/{v0/error.rs => v0.rs} | 0 runtime/devnet/src/extensions/v0/assets.rs | 76 -- runtime/devnet/src/extensions/v0/mod.rs | 2 - runtime/devnet/src/lib.rs | 101 +- runtime/testnet/Cargo.toml | 5 +- runtime/testnet/src/extensions.rs | 558 +---------- runtime/testnet/src/lib.rs | 41 +- scripts/pallet-weights-template.hbs | 122 +++ 43 files changed, 1610 insertions(+), 3291 deletions(-) create mode 100644 pallets/api/Cargo.toml create mode 100644 pallets/api/src/fungibles/benchmarking.rs create mode 100644 pallets/api/src/fungibles/mod.rs create mode 100644 pallets/api/src/fungibles/tests.rs create mode 100644 pallets/api/src/fungibles/weights.rs create mode 100644 pallets/api/src/lib.rs create mode 100644 pallets/api/src/mock.rs create mode 100755 pop-api/integration-tests/contracts/fungibles/Cargo.toml create mode 100755 pop-api/integration-tests/contracts/fungibles/lib.rs delete mode 100644 pop-api/src/v0/balances.rs delete mode 100644 pop-api/src/v0/cross_chain/coretime.rs delete mode 100644 pop-api/src/v0/cross_chain/mod.rs delete mode 100644 pop-api/src/v0/nfts.rs create mode 100644 primitives/README.md delete mode 100644 primitives/src/cross_chain.rs delete mode 100644 primitives/src/storage_keys.rs create mode 100644 runtime/devnet/src/config/api.rs rename runtime/devnet/src/extensions/{v0/error.rs => v0.rs} (100%) delete mode 100644 runtime/devnet/src/extensions/v0/assets.rs delete mode 100644 runtime/devnet/src/extensions/v0/mod.rs create mode 100644 scripts/pallet-weights-template.hbs diff --git a/Cargo.lock b/Cargo.lock index 1983b157..d3ca53e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,7 +215,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -361,9 +361,9 @@ checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -740,9 +740,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" +checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32" dependencies = [ "async-io 2.3.3", "async-lock 3.4.0", @@ -770,7 +770,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -907,7 +907,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1051,18 +1051,6 @@ dependencies = [ "piper", ] -[[package]] -name = "bounded-collections" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca548b6163b872067dc5eb82fd130c56881435e30367d2073594a3d9744120dd" -dependencies = [ - "log", - "parity-scale-codec", - "scale-info", - "serde", -] - [[package]] name = "bounded-collections" version = "0.2.0" @@ -1457,9 +1445,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" dependencies = [ "jobserver", "libc", @@ -1628,7 +1616,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1676,7 +1664,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2342,7 +2330,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2383,7 +2371,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa22d6e479a4d3a2790bab291269ba0917a1ac384255a54a2ebc3f7c37e505e" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "bp-xcm-bridge-hub-router", "cumulus-primitives-core", "frame-benchmarking", @@ -2662,7 +2650,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2702,7 +2690,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2719,7 +2707,7 @@ checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2767,7 +2755,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2789,7 +2777,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2881,7 +2869,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2894,7 +2882,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2983,7 +2971,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3007,9 +2995,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.71", + "syn 2.0.72", "termcolor", - "toml 0.8.14", + "toml 0.8.15", "walkdir", ] @@ -3219,7 +3207,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3230,7 +3218,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3413,7 +3401,7 @@ dependencies = [ "prettyplease 0.2.20", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3688,7 +3676,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3849,7 +3837,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3862,7 +3850,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3873,7 +3861,7 @@ checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4066,7 +4054,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5197,9 +5185,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -5847,7 +5835,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5861,7 +5849,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5872,7 +5860,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5883,7 +5871,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6245,7 +6233,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "synstructure 0.13.1", ] @@ -6293,7 +6281,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6616,6 +6604,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "pallet-api" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-asset-conversion" version = "11.0.0" @@ -7087,7 +7092,7 @@ checksum = "3163c6bc21b55a0ccb74c546ba784d9c9e69beb9240c059d28a3052f4cbce509" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7755,7 +7760,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7993,7 +7998,7 @@ version = "8.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba9138b04168b07b1aff4a2079f5514753c31dddba40e5fb471b9cda7da27ad6" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "frame-benchmarking", "frame-support", "frame-system", @@ -8345,7 +8350,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8386,7 +8391,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9203,7 +9208,7 @@ version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248ab090959a92e61493277e33b7e85104280a4beb4cb0815137d3c8c50a07f4" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "derive_more", "parity-scale-codec", "polkadot-core-primitives", @@ -9560,7 +9565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6380dbe1fb03ecc74ad55d841cfc75480222d153ba69ddcb00977866cbdabdb8" dependencies = [ "polkavm-derive-impl", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9572,7 +9577,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9697,7 +9702,6 @@ dependencies = [ name = "pop-primitives" version = "0.0.0" dependencies = [ - "bounded-collections 0.1.9", "parity-scale-codec", "scale-info", ] @@ -9727,7 +9731,6 @@ dependencies = [ "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", - "enumflags2", "env_logger 0.11.3", "frame-benchmarking", "frame-executive", @@ -9739,6 +9742,7 @@ dependencies = [ "hex", "hex-literal", "log", + "pallet-api", "pallet-assets", "pallet-aura", "pallet-authorship", @@ -9801,7 +9805,6 @@ dependencies = [ "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", - "enumflags2", "env_logger 0.11.3", "frame-benchmarking", "frame-executive", @@ -9864,9 +9867,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "powerfmt" @@ -9927,7 +9930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10021,7 +10024,7 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10067,7 +10070,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10135,7 +10138,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10401,7 +10404,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -11062,7 +11065,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -12076,7 +12079,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -12242,7 +12245,7 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.71", + "syn 2.0.72", "thiserror", ] @@ -12475,7 +12478,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13094,7 +13097,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13288,7 +13291,7 @@ dependencies = [ "bip39", "bitflags 1.3.2", "blake2 0.10.6", - "bounded-collections 0.2.0", + "bounded-collections", "bs58 0.5.1", "dyn-clonable", "ed25519-zebra 3.1.0", @@ -13360,7 +13363,7 @@ checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13381,7 +13384,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13630,7 +13633,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13837,7 +13840,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13860,7 +13863,7 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3be30aec904994451dcacf841a9168cfbbaf817de6b24b6a1c1418cbf1af2fe" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "parity-scale-codec", "scale-info", "serde", @@ -13969,7 +13972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fa328b87de3466bc38cc9a07244c42c647b7755b81115e1dfeb47cc13fc6e6" dependencies = [ "array-bytes 6.2.3", - "bounded-collections 0.2.0", + "bounded-collections", "derivative", "environmental", "impl-trait-for-tuples", @@ -14123,7 +14126,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14224,7 +14227,7 @@ dependencies = [ "sp-maybe-compressed-blob", "strum 0.24.1", "tempfile", - "toml 0.8.14", + "toml 0.8.15", "walkdir", "wasm-opt", ] @@ -14299,7 +14302,7 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.71", + "syn 2.0.72", "thiserror", "tokio", ] @@ -14333,7 +14336,7 @@ dependencies = [ "quote", "scale-typegen", "subxt-codegen", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14384,9 +14387,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -14413,7 +14416,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14503,9 +14506,9 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] @@ -14527,18 +14530,18 @@ checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14657,9 +14660,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", @@ -14682,7 +14685,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14754,14 +14757,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.15", + "toml_edit 0.22.16", ] [[package]] @@ -14797,15 +14800,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.14", ] [[package]] @@ -14873,7 +14876,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14918,7 +14921,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -15344,7 +15347,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -15378,7 +15381,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -15866,9 +15869,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.25" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caba658a80831539b30698ae9862a72db6697dfdd7151e46920f5f2755c3ce2" +checksum = "901e8597c777fa042e9e245bd56c0dc4418c5db3f845b6ff94fbac732c6a0692" dependencies = [ "bytemuck", "safe_arch", @@ -16155,9 +16158,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" dependencies = [ "memchr", ] @@ -16266,7 +16269,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -16315,7 +16318,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -16335,7 +16338,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6b3cde02..8b2d0a8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "runtime/devnet", "runtime/testnet", "integration-tests", + "pallets/*", "primitives", "scripts/fund-dev-accounts", ] @@ -51,6 +52,7 @@ substrate-wasm-builder = "18.0.1" substrate-build-script-utils = "11.0.0" # Local +pallet-api = { path = "pallets/api", default-features = false } pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-common = { path = "runtime/common", default-features = false } diff --git a/node/src/command.rs b/node/src/command.rs index 98af5450..8ba956e1 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -64,18 +64,9 @@ impl RuntimeResolver for PathBuf { fn load_spec(id: &str) -> std::result::Result, String> { Ok(match id { - #[cfg(not(feature = "paseo"))] - "dev-rococo" => Box::new(chain_spec::development_config(Relay::RococoLocal)), - #[cfg(feature = "paseo")] - "dev-paseo" => Box::new(chain_spec::development_config(Relay::PaseoLocal)), - #[cfg(not(feature = "paseo"))] - "pop-rococo" => Box::new(chain_spec::testnet_config(Relay::Rococo)), - #[cfg(feature = "paseo")] - "pop-paseo" => Box::new(chain_spec::testnet_config(Relay::Paseo)), - #[cfg(feature = "paseo")] + "dev" | "dev-paseo" => Box::new(chain_spec::development_config(Relay::PaseoLocal)), + "test" | "pop-paseo" => Box::new(chain_spec::testnet_config(Relay::Paseo)), "" | "local" => Box::new(chain_spec::development_config(Relay::PaseoLocal)), - #[cfg(not(feature = "paseo"))] - "" | "local" => Box::new(chain_spec::development_config(Relay::RococoLocal)), path => { let path: PathBuf = path.into(); match path.runtime() { diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml new file mode 100644 index 00000000..a813a09c --- /dev/null +++ b/pallets/api/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "pallet-api" +authors.workspace = true +description = "Api pallet, enabling smart(er) contracts with the power of Polkadot" +edition.workspace = true +license.workspace = true +version = "0.1.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec.workspace = true +scale-info.workspace = true + +# Substrate +frame-benchmarking.workspace = true +frame-support.workspace = true +frame-system.workspace = true +pallet-assets.workspace = true +sp-runtime.workspace = true +sp-std.workspace = true + +[dev-dependencies] +pallet-balances.workspace = true +sp-core.workspace = true +sp-io.workspace = true + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "pallet-assets/std", + "pallet-balances/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs new file mode 100644 index 00000000..d3d65b97 --- /dev/null +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -0,0 +1,106 @@ +//! Benchmarking setup for pallet-api::fungibles + +use super::{AccountIdOf, AssetIdOf, AssetsInstanceOf, AssetsOf, BalanceOf, Call, Config, Pallet}; +use frame_benchmarking::{account, v2::*}; +use frame_support::{ + assert_ok, + traits::{ + fungibles::{ + approvals::{Inspect as ApprovalInspect, Mutate}, + Create, Inspect, + }, + Currency, + }, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::Zero; + +const SEED: u32 = 1; + +// See if `generic_event` has been emitted. +fn assert_has_event( + generic_event: >>::RuntimeEvent, +) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + +#[benchmarks( + where + > as Inspect<::AccountId>>::AssetId: Zero, +)] +mod benchmarks { + use super::*; + + // Parameter: + // - 'a': whether `approve_transfer` is required. + // - 'c': whether `cancel_approval` is required. + #[benchmark] + fn approve(a: Linear<0, 1>, c: Linear<0, 1>) -> Result<(), BenchmarkError> { + let asset_id = AssetIdOf::::zero(); + let min_balance = >::from(1u32); + let owner: AccountIdOf = account("Alice", 0, SEED); + let spender: AccountIdOf = account("Bob", 0, SEED); + let current_allowance = >::from(u32::MAX / 2); + T::Currency::make_free_balance_be(&owner, u32::MAX.into()); + // Set the `current_allowance`. + assert_ok!( as Create>>::create( + asset_id.clone(), + owner.clone(), + true, + min_balance + )); + assert_ok!( as Mutate>>::approve( + asset_id.clone(), + &owner, + &spender, + current_allowance, + )); + let approval_value = match (a, c) { + // Equal to the current allowance. + (0, 0) => current_allowance, + // Greater than the current allowance. + (1, 0) => >::from(u32::MAX), + // Zero. + (0, 1) => >::from(0u32), + // Smaller than the current allowance. + (1, 1) => >::from(u32::MAX / 4), + _ => unreachable!("values can only be 0 or 1"), + }; + + #[extrinsic_call] + _(RawOrigin::Signed(owner.clone()), asset_id.clone(), spender.clone(), approval_value); + + assert_eq!(AssetsOf::::allowance(asset_id.clone(), &owner, &spender), approval_value); + if c == 1 { + assert_has_event::( + pallet_assets::Event::ApprovalCancelled { + asset_id: asset_id.clone(), + owner: owner.clone(), + delegate: spender.clone(), + } + .into(), + ); + } + if a == 1 { + let amount = match c { + // When the allowance was cancelled and then approved with the new value. + 1 => approval_value, + // When the allowance was increased. + 0 => approval_value - current_allowance, + _ => unreachable!("`c` can only be 0 or 1"), + }; + assert_has_event::( + pallet_assets::Event::ApprovedTransfer { + asset_id, + source: owner, + delegate: spender, + amount, + } + .into(), + ); + } + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs new file mode 100644 index 00000000..cd34664a --- /dev/null +++ b/pallets/api/src/fungibles/mod.rs @@ -0,0 +1,240 @@ +/// The fungibles pallet serves as a wrapper around the pallet_assets, offering a streamlined +/// interface for interacting with fungible assets. The goal is to provide a simplified, consistent +/// API that adheres to standards in the smart contract space. + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +mod tests; +pub mod weights; + +use frame_support::traits::fungibles::{metadata::Inspect as MetadataInspect, Inspect}; +pub use pallet::*; +use pallet_assets::WeightInfo as AssetsWeightInfoTrait; +use weights::WeightInfo; + +type AccountIdOf = ::AccountId; +type AssetIdOf = > as Inspect< + ::AccountId, +>>::AssetId; +type AssetIdParameterOf = >>::AssetIdParameter; +type AssetsOf = pallet_assets::Pallet>; +type AssetsInstanceOf = ::AssetsInstance; +type AssetsWeightInfoOf = >>::WeightInfo; +type BalanceOf = > as Inspect< + ::AccountId, +>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + dispatch::{DispatchResult, DispatchResultWithPostInfo, WithPostDispatchInfo}, + pallet_prelude::*, + traits::fungibles::approvals::Inspect as ApprovalInspect, + }; + use frame_system::pallet_prelude::*; + use sp_runtime::{ + traits::{StaticLookup, Zero}, + Saturating, + }; + use sp_std::vec::Vec; + + /// State reads for the fungibles api with required input. + #[derive(Encode, Decode, Debug, MaxEncodedLen)] + #[repr(u8)] + #[allow(clippy::unnecessary_cast)] + pub enum Read { + /// Total token supply for a given asset ID. + #[codec(index = 0)] + TotalSupply(AssetIdOf), + /// Account balance for a given asset ID. + #[codec(index = 1)] + BalanceOf { + /// The asset ID. + id: AssetIdOf, + /// The account ID of the owner. + owner: AccountIdOf, + }, + /// Allowance for a spender approved by an owner, for a given asset ID. + #[codec(index = 2)] + Allowance { + /// The asset ID. + id: AssetIdOf, + /// The account ID of the owner. + owner: AccountIdOf, + /// The account ID of the spender. + spender: AccountIdOf, + }, + /// Token name for a given asset ID. + #[codec(index = 8)] + TokenName(AssetIdOf), + /// Token symbol for a given asset ID. + #[codec(index = 9)] + TokenSymbol(AssetIdOf), + /// Token decimals for a given asset ID. + #[codec(index = 10)] + TokenDecimals(AssetIdOf), + } + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_assets::Config { + /// The instance of pallet assets it is tightly coupled to. + type AssetsInstance; + /// Weight information for dispatchables in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional + /// `data` in unspecified format. + /// + /// # Parameters + /// * `id` - The ID of the asset. + /// * `to` - The recipient account. + /// * `value` - The number of tokens to transfer. + #[pallet::call_index(3)] + #[pallet::weight(AssetsWeightInfoOf::::transfer_keep_alive())] + pub fn transfer( + origin: OriginFor, + id: AssetIdOf, + target: AccountIdOf, + amount: BalanceOf, + ) -> DispatchResult { + let target = T::Lookup::unlookup(target); + AssetsOf::::transfer_keep_alive(origin, id.into(), target, amount) + } + + /// Approves an account to spend a specified number of tokens on behalf of the caller. + /// + /// # Parameters + /// * `id` - The ID of the asset. + /// * `spender` - The account that is allowed to spend the tokens. + /// * `value` - The number of tokens to approve. + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::approve(1, 1))] + pub fn approve( + origin: OriginFor, + id: AssetIdOf, + spender: AccountIdOf, + value: BalanceOf, + ) -> DispatchResultWithPostInfo { + let weight = |approve: u32, cancel: u32| -> Weight { + ::WeightInfo::approve(cancel, approve) + }; + let who = ensure_signed(origin.clone()).map_err(|e| e.with_weight(weight(0, 0)))?; + let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); + let spender = T::Lookup::unlookup(spender); + let id: AssetIdParameterOf = id.into(); + + // If the new value is equal to the current allowance, do nothing. + let return_weight = if value == current_allowance { + weight(0, 0) + } + // If the new value is greater than the current allowance, approve the difference + // because `approve_transfer` works additively (see `pallet-assets`). + else if value > current_allowance { + AssetsOf::::approve_transfer( + origin, + id, + spender, + value.saturating_sub(current_allowance), + ) + .map_err(|e| e.with_weight(weight(1, 0)))?; + weight(1, 0) + } else { + // If the new value is less than the current allowance, cancel the approval and set the new value + AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + .map_err(|e| e.with_weight(weight(0, 1)))?; + if value.is_zero() { + return Ok(Some(weight(0, 1)).into()); + } + AssetsOf::::approve_transfer(origin, id, spender, value)?; + weight(1, 1) + }; + Ok(Some(return_weight).into()) + } + + /// Increases the allowance of a spender. + /// + /// # Parameters + /// * `id` - The ID of the asset. + /// * `spender` - The account that is allowed to spend the tokens. + /// * `value` - The number of tokens to increase the allowance by. + #[pallet::call_index(6)] + #[pallet::weight(AssetsWeightInfoOf::::approve_transfer())] + pub fn increase_allowance( + origin: OriginFor, + id: AssetIdOf, + spender: AccountIdOf, + value: BalanceOf, + ) -> DispatchResult { + let spender = T::Lookup::unlookup(spender); + AssetsOf::::approve_transfer(origin, id.into(), spender, value) + } + } + + impl Pallet { + /// Returns the total token supply for a given asset ID. + /// + /// # Parameters + /// * `id` - The ID of the asset. + pub fn total_supply(id: AssetIdOf) -> BalanceOf { + AssetsOf::::total_supply(id) + } + + /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if + /// the account is non-existent. + /// + /// # Parameters + /// * `id` - The ID of the asset. + /// * `owner` - The account whose balance is being queried. + pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { + AssetsOf::::balance(id, owner) + } + + /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given + /// asset ID. Returns `0` if no allowance has been set. + /// + /// # Parameters + /// * `id` - The ID of the asset. + /// * `owner` - The account that owns the tokens. + /// * `spender` - The account that is allowed to spend the tokens. + pub fn allowance( + id: AssetIdOf, + owner: &AccountIdOf, + spender: &AccountIdOf, + ) -> BalanceOf { + AssetsOf::::allowance(id, owner, spender) + } + + /// Returns the token name for a given asset ID. + /// + /// # Parameters + /// * `id` - The ID of the asset. + pub fn token_name(id: AssetIdOf) -> Vec { + as MetadataInspect>>::name(id) + } + + /// Returns the token symbol for a given asset ID. + /// + /// # Parameters + /// * `id` - The ID of the asset. + pub fn token_symbol(id: AssetIdOf) -> Vec { + as MetadataInspect>>::symbol(id) + } + + /// Returns the token decimals for a given asset ID. + /// + /// # Parameters + /// * `id` - The ID of the asset. + pub fn token_decimals(id: AssetIdOf) -> u8 { + as MetadataInspect>>::decimals(id) + } + } +} diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs new file mode 100644 index 00000000..dbfa0b34 --- /dev/null +++ b/pallets/api/src/fungibles/tests.rs @@ -0,0 +1,147 @@ +use crate::mock::*; +use frame_support::{ + assert_ok, + traits::fungibles::{approvals::Inspect, metadata::Inspect as MetadataInspect}, +}; + +const ASSET: u32 = 42; + +#[test] +fn transfer_works() { + new_test_ext().execute_with(|| { + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); + let balance_before_transfer = Assets::balance(ASSET, &BOB); + assert_ok!(Fungibles::transfer(signed(ALICE), ASSET, BOB, amount / 2)); + let balance_after_transfer = Assets::balance(ASSET, &BOB); + assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); + }); +} + +// Non-additive, sets new value. +#[test] +fn approve_works() { + new_test_ext().execute_with(|| { + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); + assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + // Approves an amount to spend that is lower than the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount / 2)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount / 2); + // Approves an amount to spend that is higher than the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + // Approves an amount to spend that is equal to the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + // Sets allowance to zero. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, 0)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), 0); + }); +} + +#[test] +fn increase_allowance_works() { + new_test_ext().execute_with(|| { + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); + assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); + assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + // Additive. + assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + }); +} + +#[test] +fn total_supply_works() { + new_test_ext().execute_with(|| { + create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); + assert_eq!(Assets::total_supply(ASSET), Fungibles::total_supply(ASSET)); + }); +} + +#[test] +fn balance_of_works() { + new_test_ext().execute_with(|| { + create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); + assert_eq!(Assets::balance(ASSET, ALICE), Fungibles::balance_of(ASSET, &ALICE)); + }); +} + +#[test] +fn allowance_works() { + new_test_ext().execute_with(|| { + create_asset_mint_and_approve(ALICE, ASSET, BOB, 100, ALICE, 50); + assert_eq!( + Assets::allowance(ASSET, &ALICE, &BOB), + Fungibles::allowance(ASSET, &ALICE, &BOB) + ); + }); +} + +#[test] +fn token_metadata_works() { + new_test_ext().execute_with(|| { + let name: Vec = vec![11, 12, 13]; + let symbol: Vec = vec![21, 22, 23]; + let decimals: u8 = 69; + create_asset_and_set_metadata(ALICE, ASSET, name.clone(), symbol.clone(), decimals); + assert_eq!(Assets::name(ASSET), Fungibles::token_name(ASSET)); + assert_eq!(Assets::symbol(ASSET), Fungibles::token_symbol(ASSET)); + assert_eq!(Assets::decimals(ASSET), Fungibles::token_decimals(ASSET)); + }); +} + +fn signed(account: AccountId) -> RuntimeOrigin { + RuntimeOrigin::signed(account) +} + +fn create_asset(owner: AccountId, asset_id: AssetId, min_balance: Balance) { + assert_ok!(Assets::create(signed(owner), asset_id, owner, min_balance)); +} + +fn mint_asset(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance) { + assert_ok!(Assets::mint(signed(owner), asset_id, to, value)); +} + +fn create_asset_and_mint_to(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance) { + create_asset(owner, asset_id, 1); + mint_asset(owner, asset_id, to, value) +} + +fn create_asset_mint_and_approve( + owner: AccountId, + asset_id: AssetId, + to: AccountId, + mint: Balance, + spender: AccountId, + approve: Balance, +) { + create_asset_and_mint_to(owner, asset_id, to, mint); + assert_ok!(Assets::approve_transfer(signed(to), asset_id, spender, approve,)); +} + +fn create_asset_and_set_metadata( + owner: AccountId, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) { + assert_ok!(Assets::create(signed(owner), asset_id, owner, 100)); + set_metadata_asset(owner, asset_id, name, symbol, decimals); +} + +fn set_metadata_asset( + owner: AccountId, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) { + assert_ok!(Assets::set_metadata(signed(owner), asset_id, name, symbol, decimals)); +} diff --git a/pallets/api/src/fungibles/weights.rs b/pallets/api/src/fungibles/weights.rs new file mode 100644 index 00000000..a6c31654 --- /dev/null +++ b/pallets/api/src/fungibles/weights.rs @@ -0,0 +1,94 @@ + +//! Autogenerated weights for `pallet_api::fungibles` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 33.0.0 +//! DATE: 2024-07-25, STEPS: `20`, REPEAT: `5`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `R0GUE`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/release/pop-node +// benchmark +// pallet +// --chain=dev +// --wasm-execution=compiled +// --pallet=pallet_api::fungibles +// --steps=20 +// --repeat=5 +// --json +// --template +// ./scripts/pallet-weights-template.hbs +// --output=./pallets/api/src/fungibles/weights.rs +// --extrinsic= + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_api::fungibles`. +pub trait WeightInfo { + fn approve(a: u32, c: u32, ) -> Weight; +} + +/// Weights for `pallet_api::fungibles` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 1]`. + /// The range of component `c` is `[0, 1]`. + fn approve(a: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `413 + c * (102 ±0)` + // Estimated: `3675 + c * (1797 ±0)` + // Minimum execution time: 35_000_000 picoseconds. + Weight::from_parts(1_207_482, 3675) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(34_649_659, 0).saturating_mul(a.into())) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(56_976_190, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 1797).saturating_mul(c.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 1]`. + /// The range of component `c` is `[0, 1]`. + fn approve(a: u32, c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `413 + c * (102 ±0)` + // Estimated: `3675 + c * (1797 ±0)` + // Minimum execution time: 35_000_000 picoseconds. + Weight::from_parts(1_207_482, 3675) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(34_649_659, 0).saturating_mul(a.into())) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(56_976_190, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 1797).saturating_mul(c.into())) + } +} + diff --git a/pallets/api/src/lib.rs b/pallets/api/src/lib.rs new file mode 100644 index 00000000..5cba0551 --- /dev/null +++ b/pallets/api/src/lib.rs @@ -0,0 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod fungibles; +#[cfg(test)] +mod mock; diff --git a/pallets/api/src/mock.rs b/pallets/api/src/mock.rs new file mode 100644 index 00000000..f5d155ef --- /dev/null +++ b/pallets/api/src/mock.rs @@ -0,0 +1,123 @@ +use frame_support::{ + derive_impl, parameter_types, + traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, Everything}, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; +pub(crate) type AccountId = u64; +pub(crate) type AssetId = u32; +pub(crate) type Balance = u128; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Assets: pallet_assets::, + Balances: pallet_balances, + Fungibles: crate::fungibles, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +type AssetsInstance = pallet_assets::Instance1; +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type RemoveItemsLimit = ConstU32<5>; + type AssetId = AssetId; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} +impl crate::fungibles::Config for Test { + type AssetsInstance = AssetsInstance; + type WeightInfo = (); +} + +pub(crate) const ALICE: AccountId = 1; +pub(crate) const BOB: AccountId = 2; +pub(crate) const INIT_AMOUNT: Balance = 100_000_000 * UNIT; +pub(crate) const UNIT: Balance = 10_000_000_000; + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .expect("Frame system builds valid default genesis config"); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INIT_AMOUNT), (BOB, INIT_AMOUNT)], + } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index dc48ea8a..9946abf7 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "pop-api" -description = "Easily access the power of Polkadot via the Pop Network" +description = "Enabling smart(er) contracts with the power of Polkadot" license = "GPL-3.0-only" version = "0.0.0" edition = "2021" [dependencies] -enumflags2 = { version = "0.7.7" } ink = { version = "5.0.0", default-features = false } sp-io = { version = "31.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } @@ -20,12 +19,9 @@ crate-type = ["rlib"] [features] default = ["std"] std = [ - "enumflags2/std", "ink/std", "pop-primitives/std", "sp-io/std", ] -assets = ["pop-primitives/assets"] -balances = [] -nfts = ["pop-primitives/nfts"] -cross-chain = ["pop-primitives/cross-chain"] +assets = [] +fungibles = ["assets"] diff --git a/pop-api/examples/fungibles/Cargo.toml b/pop-api/examples/fungibles/Cargo.toml index 565b0554..19d9ce12 100755 --- a/pop-api/examples/fungibles/Cargo.toml +++ b/pop-api/examples/fungibles/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] ink = { version = "5.0.0", default-features = false } -pop-api = { path = "../../../pop-api", default-features = false, features = ["assets"] } +pop-api = { path = "../../../pop-api", default-features = false, features = ["fungibles"] } [lib] path = "lib.rs" diff --git a/pop-api/integration-tests/Cargo.toml b/pop-api/integration-tests/Cargo.toml index 94c0ba83..cc56630a 100644 --- a/pop-api/integration-tests/Cargo.toml +++ b/pop-api/integration-tests/Cargo.toml @@ -11,7 +11,7 @@ frame-system = { version = "29.0.0", default-features = false } pallet-balances = { version = "29.0.2", default-features = false } pallet-assets = { version = "30.0.0", default-features = false } pallet-contracts = { version = "28.0.0", default-features = false } -pop-primitives = { path = "../../primitives", default-features = false, features = ["assets"] } +pop-primitives = { path = "../../primitives", default-features = false } pop-runtime-devnet = { path = "../../runtime/devnet", default-features = false } sp-io = { version = "31.0.0", default-features = false } sp-runtime = { version = "32.0.0", default-features = false } diff --git a/pop-api/integration-tests/contracts/fungibles/Cargo.toml b/pop-api/integration-tests/contracts/fungibles/Cargo.toml new file mode 100755 index 00000000..7c322004 --- /dev/null +++ b/pop-api/integration-tests/contracts/fungibles/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fungibles" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "5.0.0", default-features = false } +pop-api = { path = "../../../../pop-api", default-features = false, features = ["fungibles"] } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "pop-api/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs new file mode 100755 index 00000000..1b42fec4 --- /dev/null +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -0,0 +1,185 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +/// Local Fungibles: +/// 1. PSP-22 Interface +/// 2. PSP-22 Metadata Interface +/// 3. Asset Management +/// +use ink::prelude::vec::Vec; +use pop_api::{ + assets::fungibles::{self as api}, + primitives::AssetId, + StatusCode, +}; + +pub type Result = core::result::Result; + +#[ink::contract] +mod fungibles { + use super::*; + + #[ink(storage)] + #[derive(Default)] + pub struct Fungibles; + + impl Fungibles { + #[ink(constructor, payable)] + pub fn new() -> Self { + ink::env::debug_println!("PopApiFungiblesExample::new"); + Default::default() + } + + /// 1. PSP-22 Interface: + /// - total_supply + /// - balance_of + /// - allowance + /// - transfer + /// - transfer_from + /// - approve + /// - increase_allowance + /// - decrease_allowance + + #[ink(message)] + pub fn total_supply(&self, id: AssetId) -> Result { + api::total_supply(id) + } + + #[ink(message)] + pub fn balance_of(&self, id: AssetId, owner: AccountId) -> Result { + api::balance_of(id, owner) + } + + #[ink(message)] + pub fn allowance( + &self, + id: AssetId, + owner: AccountId, + spender: AccountId, + ) -> Result { + api::allowance(id, owner, spender) + } + + #[ink(message)] + pub fn transfer(&self, id: AssetId, to: AccountId, value: Balance) -> Result<()> { + api::transfer(id, to, value) + } + + #[ink(message)] + pub fn transfer_from( + &self, + id: AssetId, + from: AccountId, + to: AccountId, + value: Balance, + // In the PSP-22 standard a `[u8]`, but the size needs to be known at compile time. + _data: Vec, + ) -> Result<()> { + api::transfer_from(id, from, to, value) + } + + #[ink(message)] + pub fn approve(&self, id: AssetId, spender: AccountId, value: Balance) -> Result<()> { + api::approve(id, spender, value) + } + + #[ink(message)] + pub fn increase_allowance( + &self, + id: AssetId, + spender: AccountId, + value: Balance, + ) -> Result<()> { + api::increase_allowance(id, spender, value) + } + + #[ink(message)] + pub fn decrease_allowance( + &self, + id: AssetId, + spender: AccountId, + value: Balance, + ) -> Result<()> { + api::decrease_allowance(id, spender, value) + } + + /// 2. PSP-22 Metadata Interface: + /// - token_name + /// - token_symbol + /// - token_decimals + + #[ink(message)] + pub fn token_name(&self, id: AssetId) -> Result> { + api::token_name(id) + } + + #[ink(message)] + pub fn token_symbol(&self, id: AssetId) -> Result> { + api::token_symbol(id) + } + + #[ink(message)] + pub fn token_decimals(&self, id: AssetId) -> Result { + api::token_decimals(id) + } + + // 3. Asset Management: + // - create + // - start_destroy + // - destroy_accounts + // - destroy_approvals + // - finish_destroy + // - set_metadata + // - clear_metadata + + // #[ink(message)] + // pub fn create(&self, id: AssetId, admin: AccountId, min_balance: Balance) -> Result<()> { + // ink::env::debug_println!( + // "PopApiFungiblesExample::create: id: {:?} admin: {:?} min_balance: {:?}", + // id, + // admin, + // min_balance, + // ); + // let result = api::create(id, admin, min_balance); + // ink::env::debug_println!("Result: {:?}", result); + // result.map_err(|e| e.into()) + // result + // } + + // #[ink(message)] + // pub fn set_metadata( + // &self, + // id: AssetId, + // name: Vec, + // symbol: Vec, + // decimals: u8, + // ) -> Result<()> { + // ink::env::debug_println!( + // "PopApiFungiblesExample::set_metadata: id: {:?} name: {:?} symbol: {:?}, decimals: {:?}", + // id, + // name, + // symbol, + // decimals, + // ); + // let result = api::set_metadata(id, name, symbol, decimals); + // ink::env::debug_println!("Result: {:?}", result); + // // result.map_err(|e| e.into()) + // result + // } + // + // #[ink(message)] + // pub fn asset_exists(&self, id: AssetId) -> Result { + // // api::asset_exists(id).map_err(|e| e.into()) + // api::asset_exists(id) + // } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn default_works() { + PopApiFungiblesExample::new(); + } + } +} diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 8f0384c0..c62f0713 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -6,6 +6,7 @@ use pop_primitives::error::{ }; const ASSET_ID: AssetId = 1; +const CONTRACT: &str = "contracts/fungibles/target/ink/fungibles.wasm"; fn decoded(result: ExecReturnValue) -> T { match ::decode(&mut &result.data[2..]) { @@ -79,19 +80,16 @@ fn transfer( result } -fn transfer_from( +fn approve( addr: AccountId32, asset_id: AssetId, - from: Option, - to: Option, + spender: AccountId32, value: Balance, - data: &[u8], ) -> ExecReturnValue { - let function = function_selector("transfer_from"); - let params = - [function, asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()] - .concat(); - bare_call(addr, params, 0).expect("should work") + let function = function_selector("approve"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result } fn increase_allowance( @@ -263,15 +261,10 @@ fn token_decimals_asset(asset_id: AssetId) -> u8 { /// - decrease_allowance #[test] -#[ignore] fn total_supply_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate( - "../../pop-api/examples/fungibles/target/ink/fungibles.wasm", - INIT_VALUE, - vec![], - ); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!(Assets::total_supply(ASSET_ID), total_supply(addr.clone(), ASSET_ID)); @@ -285,12 +278,10 @@ fn total_supply_works() { } #[test] -#[ignore] fn balance_of_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!(Assets::balance(ASSET_ID, BOB), balance_of(addr.clone(), ASSET_ID, BOB)); @@ -304,12 +295,10 @@ fn balance_of_works() { } #[test] -#[ignore] fn allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!( @@ -329,12 +318,10 @@ fn allowance_works() { } #[test] -#[ignore] fn transfer_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. @@ -362,11 +349,11 @@ fn transfer_works() { Module { index: 52, error: 0 }, ); // Successful transfer. - let bob_balance_before_mint = Assets::balance(asset, &BOB); + let balance_before_transfer = Assets::balance(asset, &BOB); let result = transfer(addr.clone(), asset, BOB, amount / 2); assert!(!result.did_revert(), "Contract reverted!"); - let bob_balance_after_mint = Assets::balance(asset, &BOB); - assert_eq!(bob_balance_after_mint, bob_balance_before_mint + amount / 2); + let balance_after_transfer = Assets::balance(asset, &BOB); + assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); // Transfer asset to account that does not exist. assert_eq!( decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), @@ -382,82 +369,65 @@ fn transfer_works() { } #[test] -#[ignore] -fn transfer_from_works() { +fn approve_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, 0, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. assert_eq!( - decoded::(transfer(addr.clone(), 1, BOB, amount,)), + decoded::(approve(addr.clone(), 0, BOB, amount)), Module { index: 52, error: 3 }, ); + let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); + assert_eq!(decoded::(approve(addr.clone(), asset, BOB, amount)), ConsumerRemaining); + + let addr = instantiate(CONTRACT, INIT_VALUE, vec![1]); // Create asset with Alice as owner and mint `amount` to contract address. let asset = create_asset_and_mint_to(ALICE, 1, addr.clone(), amount); // Asset is not live, i.e. frozen or being destroyed. freeze_asset(ALICE, asset); assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount,)), + decoded::(approve(addr.clone(), asset, BOB, amount)), Module { index: 52, error: 16 }, ); thaw_asset(ALICE, asset); - // Not enough balance. - assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount + 1 * UNIT)), - Module { index: 52, error: 0 }, - ); - // Not enough balance due to ED. - assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 0 }, - ); - // Successful transfer. - let bob_balance_before_mint = Assets::balance(asset, &BOB); - let result = transfer(addr.clone(), asset, BOB, amount / 2); - assert!(!result.did_revert(), "Contract reverted!"); - let bob_balance_after_mint = Assets::balance(asset, &BOB); - assert_eq!(bob_balance_after_mint, bob_balance_before_mint + amount / 2); - // Transfer asset to account that does not exist. - assert_eq!( - decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), - Token(CannotCreate) - ); + // Successful approvals: + assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); + assert!(!approve(addr.clone(), asset, BOB, amount).did_revert(), "Contract reverted!"); + assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); + // Non-additive, sets new value. + assert!(!approve(addr.clone(), asset, BOB, amount / 2).did_revert(), "Contract reverted!"); + assert_eq!(Assets::allowance(asset, &addr, &BOB), amount / 2); // Asset is not live, i.e. frozen or being destroyed. start_destroy_asset(ALICE, asset); assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount / 4)), + decoded::(approve(addr.clone(), asset, BOB, amount)), Module { index: 52, error: 16 }, ); }); } #[test] -#[ignore] fn increase_allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("../examples/fungibles/target/ink/fungibles.wasm", 0, vec![]); + let addr = instantiate(CONTRACT, 0, vec![]); let amount: Balance = 100 * UNIT; - let asset = 0; - create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); + // Asset does not exist. assert_eq!( - decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - ConsumerRemaining + decoded::(increase_allowance(addr.clone(), 0, BOB, amount)), + Module { index: 52, error: 3 }, ); - - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); - // Asset does not exist. - let asset = 1; + let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); assert_eq!( decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 3 }, + ConsumerRemaining ); + + let addr = instantiate(CONTRACT, INIT_VALUE, vec![1]); // Create asset with Alice as owner and mint `amount` to contract address. - create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); + let asset = create_asset_and_mint_to(ALICE, 1, addr.clone(), amount); // Asset is not live, i.e. frozen or being destroyed. freeze_asset(ALICE, asset); assert_eq!( @@ -465,7 +435,7 @@ fn increase_allowance_works() { Module { index: 52, error: 16 }, ); thaw_asset(ALICE, asset); - // Successful approval. + // Successful approvals: assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); assert!( !increase_allowance(addr.clone(), asset, BOB, amount).did_revert(), @@ -493,15 +463,10 @@ fn increase_allowance_works() { /// - token_decimals #[test] -#[ignore] fn token_metadata_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate( - "../../pop-api/examples/fungibles/target/ink/fungibles.wasm", - INIT_VALUE, - vec![], - ); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let name: Vec = vec![11, 12, 13]; let symbol: Vec = vec![21, 22, 23]; @@ -536,7 +501,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate(CONTRACT, INIT_VALUE, vec![]); // // // No tokens in circulation. // assert_eq!(Assets::asset_exists(ASSET_ID), asset_exists(addr.clone(), ASSET_ID)); @@ -553,7 +518,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate(CONTRACT, INIT_VALUE, vec![]); // let amount: Balance = 100 * UNIT; // // // Asset does not exist. @@ -581,11 +546,11 @@ fn token_metadata_works() { // ); // thaw_asset(addr.clone(), asset); // // Successful mint. -// let bob_balance_before_mint = Assets::balance(asset, &BOB); +// let balance_before_mint = Assets::balance(asset, &BOB); // let result = transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8]); // assert!(!result.did_revert(), "Contract reverted!"); -// let bob_balance_after_mint = Assets::balance(asset, &BOB); -// assert_eq!(bob_balance_after_mint, bob_balance_before_mint + amount); +// let balance_after_mint = Assets::balance(asset, &BOB); +// assert_eq!(balance_after_mint, balance_before_mint + amount); // // Can not mint more tokens than Balance::MAX. // assert_eq!( // decoded::(transfer_from( @@ -613,14 +578,14 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // // Instantiate a contract without balance (relay token). -// let addr = instantiate("../examples/fungibles/target/ink/fungibles.wasm", 0, vec![0]); +// let addr = instantiate(CONTRACT, 0, vec![0]); // // No balance to pay for fees. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), // Module { index: 10, error: 2 }, // ); // // Instantiate a contract without balance (relay token). -// let addr = instantiate("../examples/fungibles/target/ink/fungibles.wasm", 100, vec![2]); +// let addr = instantiate(CONTRACT, 100, vec![2]); // // No balance to pay the deposit. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), @@ -628,7 +593,7 @@ fn token_metadata_works() { // ); // // Instantiate a contract with balance. // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); +// instantiate(CONTRACT, INIT_VALUE, vec![1]); // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, BOB, 0)), // Module { index: 52, error: 7 }, @@ -652,7 +617,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate(CONTRACT, INIT_VALUE, vec![]); // // create_asset(addr.clone(), ASSET_ID, 1); // let result = set_metadata(addr.clone(), ASSET_ID, vec![12], vec![12], 12); diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index 86621c51..a984bb9e 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -1,17 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -use ink::env::chain_extension::FromStatusCode; - use constants::DECODING_FAILED; - +use ink::env::chain_extension::FromStatusCode; #[cfg(feature = "assets")] pub use v0::assets; -#[cfg(feature = "balances")] -pub use v0::balances; -#[cfg(feature = "cross-chain")] -pub use v0::cross_chain; -#[cfg(feature = "nfts")] -pub use v0::nfts; pub mod primitives; pub mod v0; @@ -22,7 +14,8 @@ pub type Result = core::result::Result; mod constants { // Errors: pub(crate) const DECODING_FAILED: u32 = 255; - pub(crate) const MODULE_ERROR: u8 = 3; + // TODO: will be used in the future when the remaining fungibles features will be implemented. + pub(crate) const _MODULE_ERROR: u8 = 3; // Function IDs: pub(crate) const DISPATCH: u8 = 0; @@ -31,6 +24,7 @@ mod constants { // Modules: pub(crate) const ASSETS: u8 = 52; pub(crate) const BALANCES: u8 = 10; + pub(crate) const FUNGIBLES: u8 = 150; } /// Represents a status code returned by the runtime. diff --git a/pop-api/src/primitives.rs b/pop-api/src/primitives.rs index 33285044..a3d596a5 100644 --- a/pop-api/src/primitives.rs +++ b/pop-api/src/primitives.rs @@ -1,8 +1,5 @@ use ink::env::{DefaultEnvironment, Environment}; - pub use pop_primitives::*; pub(crate) type AccountId = ::AccountId; pub(crate) type Balance = ::Balance; -#[cfg(any(feature = "nfts", feature = "cross-chain"))] -type BlockNumber = ::BlockNumber; diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 255e8502..42373cd7 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -1,26 +1,55 @@ -use ink::prelude::vec::Vec; +use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; use crate::{ - assets, - constants::{ASSETS, BALANCES, MODULE_ERROR}, + constants::{ASSETS, BALANCES, DECODING_FAILED, DISPATCH, FUNGIBLES, READ_STATE}, primitives::{AccountId, AssetId, Balance}, + v0::V0, Result, StatusCode, }; +use constants::*; +pub use metadata::*; /// Local Fungibles: /// 1. PSP-22 Interface /// 2. PSP-22 Metadata Interface /// 3. Asset Management -/// 1. PSP-22 Interface: -/// - total_supply -/// - balance_of -/// - allowance -/// - transfer -/// - transfer_from -/// - approve -/// - increase_allowance -/// - decrease_allowance +mod constants { + /// 1. PSP-22 Interface: + /// - total_supply + pub const TOTAL_SUPPLY: u8 = 0; + /// - balance_of + pub const BALANCE_OF: u8 = 1; + /// - allowance + pub const ALLOWANCE: u8 = 2; + /// - transfer + pub(super) const TRANSFER: u8 = 3; + /// - transfer_from + pub(super) const TRANSFER_FROM: u8 = 4; + /// - approve + pub(super) const APPROVE: u8 = 5; + /// - increase_allowance + pub(super) const INCREASE_ALLOWANCE: u8 = 6; + /// - decrease_allowance + pub(super) const DECREASE_ALLOWANCE: u8 = 7; + + /// 2. PSP-22 Metadata Interface: + /// - token_name + pub const TOKEN_NAME: u8 = 8; + /// - token_symbol + pub const TOKEN_SYMBOL: u8 = 9; + /// - token_decimals + pub const TOKEN_DECIMALS: u8 = 10; + + // 3. Asset Management: + // - create + // - start_destroy + // - destroy_accounts + // - destroy_approvals + // - finish_destroy + // - set_metadata + // - clear_metadata +} /// Returns the total token supply for a given asset ID. /// @@ -31,7 +60,12 @@ use crate::{ /// The total supply of the token, or an error if the operation fails. #[inline] pub fn total_supply(id: AssetId) -> Result { - assets::total_supply(id) + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOTAL_SUPPLY])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if @@ -45,7 +79,12 @@ pub fn total_supply(id: AssetId) -> Result { /// The balance of the specified account, or an error if the operation fails. #[inline] pub fn balance_of(id: AssetId, owner: AccountId) -> Result { - assets::balance_of(id, owner) + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, BALANCE_OF])) + .input::<(AssetId, AccountId)>() + .output::>, true>() + .handle_error_code::() + .call(&(id, owner)) + .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given @@ -60,7 +99,12 @@ pub fn balance_of(id: AssetId, owner: AccountId) -> Result { /// The remaining allowance, or an error if the operation fails. #[inline] pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result { - assets::allowance(id, owner, spender) + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, ALLOWANCE])) + .input::<(AssetId, AccountId, AccountId)>() + .output::>, true>() + .handle_error_code::() + .call(&(id, owner, spender)) + .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional @@ -74,8 +118,12 @@ pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result Result<()> { - assets::transfer_keep_alive(id, to, value) +pub fn transfer(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, target, amount)) } /// Transfers `value` tokens on behalf of `from` to account `to` with additional `data` @@ -91,8 +139,12 @@ pub fn transfer(id: AssetId, to: AccountId, value: Balance) -> Result<()> { /// # Returns /// Returns `Ok(())` if successful, or an error if the transfer fails. #[inline] -pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, value: Balance) -> Result<()> { - assets::transfer_approved(id, from, to, value) +pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER_FROM])) + .input::<(AssetId, AccountId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, from, to, amount)) } /// Approves an account to spend a specified number of tokens on behalf of the caller. @@ -105,9 +157,12 @@ pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, value: Balance /// # Returns /// Returns `Ok(())` if successful, or an error if the approval fails. #[inline] -pub fn approve(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - assets::cancel_approval(id, spender)?; - assets::approve_transfer(id, spender, value) +pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, APPROVE])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, spender, amount)) } /// Increases the allowance of a spender. @@ -121,7 +176,11 @@ pub fn approve(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - assets::approve_transfer(id, spender, value) + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, INCREASE_ALLOWANCE])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, spender, value)) } /// Decreases the allowance of a spender. @@ -134,63 +193,69 @@ pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] -pub fn decrease_allowance(_id: AssetId, _spender: AccountId, _value: Balance) -> Result<()> { - // let allowance = assets::allowance(id, owner, spender)?; - // assets::cancel_approval(id, spender.clone())?; - // assets::approve_transfer(id, spender, value) - Ok(()) +pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, DECREASE_ALLOWANCE])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, spender, value)) } -/// 2. PSP-22 Metadata Interface: -/// - token_name -/// - token_symbol -/// - token_decimals - -/// Returns the token name for a given asset ID. -/// -/// # Arguments -/// * `id` - The ID of the asset. -/// -/// # Returns -/// The name of the token as a byte vector, or an error if the operation fails. -#[inline] -pub fn token_name(id: AssetId) -> Result> { - assets::token_name(id) -} +pub mod metadata { + use super::*; + /// Returns the token name for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The name of the token as a byte vector, or an error if the operation fails. + #[inline] + pub fn token_name(id: AssetId) -> Result> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_NAME])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) + } -/// Returns the token symbol for a given asset ID. -/// -/// # Arguments -/// * `id` - The ID of the asset. -/// -/// # Returns -/// The symbol of the token as a byte vector, or an error if the operation fails. -#[inline] -pub fn token_symbol(id: AssetId) -> Result> { - assets::token_symbol(id) -} + /// Returns the token symbol for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The symbol of the token as a byte vector, or an error if the operation fails. + #[inline] + pub fn token_symbol(id: AssetId) -> Result> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_SYMBOL])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) + } -/// Returns the token decimals for a given asset ID. -/// -/// # Arguments -/// * `id` - The ID of the asset. -/// -/// # Returns -/// The number of decimals of the token as a byte vector, or an error if the operation fails. -#[inline] -pub fn token_decimals(id: AssetId) -> Result { - assets::token_decimals(id) + /// Returns the token decimals for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The number of decimals of the token as a byte vector, or an error if the operation fails. + #[inline] + pub fn token_decimals(id: AssetId) -> Result { + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_DECIMALS])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| ::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) + } } -// /// 3. Asset Management: -// /// - create -// /// - start_destroy -// /// - destroy_accounts -// /// - destroy_approvals -// /// - finish_destroy -// /// - set_metadata -// /// - clear_metadata -// +// pub asset_management { // /// Create a new token with a given asset ID. // /// // /// # Arguments @@ -200,9 +265,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the creation fails. -// // pub fn create(id: AssetId, admin: impl Into>, min_balance: Balance) -> Result<()> { -// // assets::create(id, admin, min_balance) -// // } +// pub fn create(id: AssetId, admin: impl Into>, min_balance: Balance) -> Result<()> { +// Ok(()) +// } // // /// Start the process of destroying a token with a given asset ID. // /// @@ -211,11 +276,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn start_destroy(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::StartDestroy { -// // id: id.into(), -// // }))?) -// // } +// fn start_destroy(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Destroy all accounts associated with a token with a given asset ID. // /// @@ -224,11 +287,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn destroy_accounts(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::DestroyAccounts { -// // id: id.into(), -// // }))?) -// // } +// fn destroy_accounts(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Destroy all approvals associated with a token with a given asset ID. // /// @@ -237,11 +298,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn destroy_approvals(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::DestroyApprovals { -// // id: id.into(), -// // }))?) -// // } +// fn destroy_approvals(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Complete the process of destroying a token with a given asset ID. // /// @@ -250,11 +309,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn finish_destroy(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::FinishDestroy { -// // id: id.into(), -// // }))?) -// // } +// fn finish_destroy(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Set the metadata for a token with a given asset ID. // /// @@ -263,9 +320,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // pub fn set_metadata(id: AssetId, name: Vec, symbol: Vec, decimals: u8) -> Result<()> { -// // assets::set_metadata(id, name, symbol, decimals) -// // } +// pub fn set_metadata(id: AssetId, name: Vec, symbol: Vec, decimals: u8) -> Result<()> { +// Ok(()) +// } // // /// Clear the metadata for a token with a given asset ID. // /// @@ -274,11 +331,10 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn clear_metadata(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::ClearMetadata { -// // id: id.into(), -// // }))?) -// // } +// fn clear_metadata(id: AssetId) -> Result<()> { +// Ok(()) +// } +// } // // pub fn asset_exists(id: AssetId) -> Result { // assets::asset_exists(id) @@ -332,15 +388,15 @@ impl From for FungiblesError { let encoded = value.0.to_le_bytes(); match encoded { // Balances. - [MODULE_ERROR, BALANCES, 2, _] => FungiblesError::NoBalance, + [_, BALANCES, 2, _] => FungiblesError::NoBalance, // Assets. - [MODULE_ERROR, ASSETS, 0, _] => FungiblesError::NoAccount, - [MODULE_ERROR, ASSETS, 1, _] => FungiblesError::NoPermission, - [MODULE_ERROR, ASSETS, 2, _] => FungiblesError::Unknown, - [MODULE_ERROR, ASSETS, 3, _] => FungiblesError::InUse, - [MODULE_ERROR, ASSETS, 5, _] => FungiblesError::MinBalanceZero, - [MODULE_ERROR, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, - [MODULE_ERROR, ASSETS, 10, _] => FungiblesError::AssetNotLive, + [_, ASSETS, 0, _] => FungiblesError::NoAccount, + [_, ASSETS, 1, _] => FungiblesError::NoPermission, + [_, ASSETS, 2, _] => FungiblesError::Unknown, + [_, ASSETS, 3, _] => FungiblesError::InUse, + [_, ASSETS, 5, _] => FungiblesError::MinBalanceZero, + [_, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, + [_, ASSETS, 10, _] => FungiblesError::AssetNotLive, _ => FungiblesError::Other(value), } } @@ -407,7 +463,7 @@ mod tests { Corruption, Unavailable, RootNotAllowed, - UnknownFunctionCall, + UnknownCall, DecodingFailed, ]; for error in other_errors { diff --git a/pop-api/src/v0/assets/mod.rs b/pop-api/src/v0/assets/mod.rs index 00021fc0..197db710 100644 --- a/pop-api/src/v0/assets/mod.rs +++ b/pop-api/src/v0/assets/mod.rs @@ -1,235 +1,2 @@ -use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; - -use crate::{ - constants::{ASSETS, DECODING_FAILED, DISPATCH, READ_STATE}, - primitives::{AccountId, AssetId, Balance}, - v0::V0, - Result, StatusCode, -}; - +#[cfg(feature = "fungibles")] pub mod fungibles; - -/// [Pallet Assets](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/assets/src/lib.rs): -/// 1. Dispatchables -/// 2. Read state functions -/// -/// 1. Dispatchables within pallet assets (TrustBackedAssets instance): -/// - create -/// - start_destroy -/// - destroy_accounts -/// - destroy_approvals -/// - finish_destroy -/// - mint -/// - burn -/// - transfer -/// - transfer_keep_alive -const TRANSFER_KEEP_ALIVE: u8 = 9; -/// - set_metadata -/// - clear_metadata -/// - approve_transfer -const APPROVE_TRANSFER: u8 = 22; -/// - cancel_approval -const CANCEL_APPROVAL: u8 = 23; -/// - transfer_approved -const TRANSFER_APPROVED: u8 = 25; - -/// Issue a new class of fungible assets from a public origin. -// pub(crate) fn create( -// id: AssetId, -// admin: impl Into>, -// min_balance: Balance, -// ) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::Create { -// id: id.into(), -// admin: admin.into(), -// min_balance, -// })) -// } -// -// /// Start the process of destroying a fungible asset class. -// pub(crate) fn start_destroy(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::StartDestroy { id: id.into() })) -// } -// -// /// Destroy all accounts associated with a given asset. -// pub(crate) fn destroy_accounts(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::DestroyAccounts { id: id.into() })) -// } -// -// /// Destroy all approvals associated with a given asset up to the max (see runtime configuration Assets `RemoveItemsLimit`). -// pub(crate) fn destroy_approvals(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::DestroyApprovals { id: id.into() })) -// } -// -// /// Complete destroying asset and unreserve currency. -// pub(crate) fn finish_destroy(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::FinishDestroy { id: id.into() })) -// } - -// /// Mint assets of a particular class. -// pub(crate) fn mint( -// id: AssetId, -// beneficiary: impl Into>, -// amount: Balance, -// ) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::Mint { -// id: id.into(), -// beneficiary: beneficiary.into(), -// amount: Compact(amount), -// })) -// } -// -// /// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`. -// pub(crate) fn burn(id: AssetId, who: impl Into>, amount: Balance) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::Burn { -// id: id.into(), -// who: who.into(), -// amount: Compact(amount), -// })) -// } - -/// Move some assets from the sender account to another, keeping the sender account alive. -#[inline] -pub fn transfer_keep_alive(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([ - V0, - DISPATCH, - ASSETS, - // E.D. is always respected with transferring tokens via the API. - TRANSFER_KEEP_ALIVE, - ])) - .input::<(AssetId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, target, amount)) -} - -// /// Set the metadata for an asset. -// pub(crate) fn set_metadata( -// id: AssetId, -// name: Vec, -// symbol: Vec, -// decimals: u8, -// ) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::SetMetadata { id: id.into(), name, symbol, decimals })) -// } - -// /// Clear the metadata for an asset. -// pub(crate) fn clear_metadata(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::ClearMetadata { id: id.into() })) -// } - -/// Approve an amount of asset for transfer by a delegated third-party account. -#[inline] -pub fn approve_transfer(id: AssetId, delegate: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, ASSETS, APPROVE_TRANSFER])) - .input::<(AssetId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, delegate, amount)) -} - -/// Cancel all of some asset approved for delegated transfer by a third-party account. -#[inline] -pub fn cancel_approval(id: AssetId, delegate: AccountId) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, ASSETS, CANCEL_APPROVAL])) - .input::<(AssetId, AccountId)>() - .output::, true>() - .handle_error_code::() - .call(&(id, delegate)) -} - -/// Transfer some asset balance from a previously delegated account to some third-party -/// account. -#[inline] -pub fn transfer_approved( - id: AssetId, - from: AccountId, - to: AccountId, - amount: Balance, -) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, ASSETS, TRANSFER_APPROVED])) - .input::<(AssetId, AccountId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, from, to, amount)) -} - -/// 2. Read state functions: -/// - total_supply -const TOTAL_SUPPLY: u8 = 0; -/// - balance_of -const BALANCE_OF: u8 = 1; -/// - allowance -const ALLOWANCE: u8 = 2; -/// - token_name -const TOKEN_NAME: u8 = 3; -/// - token_symbol -const TOKEN_SYMBOL: u8 = 4; -/// - token_decimals -const TOKEN_DECIMALS: u8 = 5; -/// - asset_exists - -#[inline] -pub fn total_supply(id: AssetId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOTAL_SUPPLY])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn balance_of(id: AssetId, owner: AccountId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, BALANCE_OF])) - .input::<(AssetId, AccountId)>() - .output::>, true>() - .handle_error_code::() - .call(&(id, owner)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, ALLOWANCE])) - .input::<(AssetId, AccountId, AccountId)>() - .output::>, true>() - .handle_error_code::() - .call(&(id, owner, spender)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn token_name(id: AssetId) -> Result> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOKEN_NAME])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} -// -#[inline] -pub fn token_symbol(id: AssetId) -> Result> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOKEN_SYMBOL])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn token_decimals(id: AssetId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOKEN_DECIMALS])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| ::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -// pub(crate) fn asset_exists(id: AssetId) -> Result { -// state::read(RuntimeStateKeys::Assets(AssetsKeys::AssetExists(id))) -// } diff --git a/pop-api/src/v0/balances.rs b/pop-api/src/v0/balances.rs deleted file mode 100644 index bf029178..00000000 --- a/pop-api/src/v0/balances.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::{ - dispatch, primitives::MultiAddress, v0::RuntimeCall, AccountId, PopApiError, - PopApiError::UnknownStatusCode, -}; - -type Result = core::result::Result; - -pub fn transfer_keep_alive( - dest: impl Into>, - value: u128, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Balances(BalancesCall::TransferKeepAlive { - dest: dest.into(), - value, - }))?) -} - -#[derive(scale::Encode)] -#[allow(dead_code)] -pub(crate) enum BalancesCall { - #[codec(index = 3)] - TransferKeepAlive { - dest: MultiAddress, - #[codec(compact)] - value: u128, - }, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// Vesting balance too high to send value. - VestingBalance, - /// Account liquidity restrictions prevent withdrawal. - LiquidityRestrictions, - /// Balance too low to send value. - InsufficientBalance, - /// Value too low to create account due to existential deposit. - ExistentialDeposit, - /// Transfer/payment would kill account. - Expendability, - /// A vesting schedule already exists for this account. - ExistingVestingSchedule, - /// Beneficiary account must pre-exist. - DeadAccount, - /// Number of named reserves exceed `MaxReserves`. - TooManyReserves, - /// Number of holds exceed `VariantCountOf`. - TooManyHolds, - /// Number of freezes exceed `MaxFreezes`. - TooManyFreezes, - /// The issuance cannot be modified since it is already deactivated. - IssuanceDeactivated, - /// The delta cannot be zero. - DeltaZero, -} - -impl TryFrom for Error { - type Error = PopApiError; - - fn try_from(status_code: u32) -> core::result::Result { - use Error::*; - match status_code { - 0 => Ok(VestingBalance), - 1 => Ok(LiquidityRestrictions), - 2 => Ok(InsufficientBalance), - 3 => Ok(ExistentialDeposit), - 4 => Ok(Expendability), - 5 => Ok(ExistingVestingSchedule), - 6 => Ok(DeadAccount), - 7 => Ok(TooManyReserves), - 8 => Ok(TooManyHolds), - 9 => Ok(TooManyFreezes), - 10 => Ok(IssuanceDeactivated), - 11 => Ok(DeltaZero), - _ => Err(UnknownStatusCode(status_code)), - } - } -} - -impl From for Error { - fn from(error: PopApiError) -> Self { - match error { - PopApiError::Balances(e) => e, - _ => panic!("expected balances error"), - } - } -} diff --git a/pop-api/src/v0/cross_chain/coretime.rs b/pop-api/src/v0/cross_chain/coretime.rs deleted file mode 100644 index 0039ed20..00000000 --- a/pop-api/src/v0/cross_chain/coretime.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::{ - primitives::cross_chain::{CrossChainMessage, OnDemand, RelayChainMessage}, - send_xcm, -}; - -/// Send a cross-chain message to place a sport order for instantaneous coretime. -pub fn place_spot_order(max_amount: u128, para_id: u32) -> crate::cross_chain::Result<()> { - Ok(send_xcm(CrossChainMessage::Relay(RelayChainMessage::OnDemand( - OnDemand::PlaceOrderKeepAlive { max_amount, para_id }, - )))?) -} diff --git a/pop-api/src/v0/cross_chain/mod.rs b/pop-api/src/v0/cross_chain/mod.rs deleted file mode 100644 index 6732c119..00000000 --- a/pop-api/src/v0/cross_chain/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -pub mod coretime; - -use crate::{PopApiError::UnknownStatusCode, *}; - -type Result = core::result::Result; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// The desired destination was unreachable, generally because there is a no way of routing - /// to it. - Unreachable, - /// There was some other issue (i.e. not to do with routing) in sending the message. - /// Perhaps a lack of space for buffering the message. - SendFailure, - /// The message execution fails the filter. - Filtered, - /// The message's weight could not be determined. - UnweighableMessage, - /// The destination `Location` provided cannot be inverted. - DestinationNotInvertible, - /// The assets to be sent are empty. - Empty, - /// Could not re-anchor the assets to declare the fees for the destination chain. - CannotReanchor, - /// Too many assets have been attempted for transfer. - TooManyAssets, - /// Origin is invalid for sending. - InvalidOrigin, - /// The version of the `Versioned` value used is not able to be interpreted. - BadVersion, - /// The given location could not be used (e.g. because it cannot be expressed in the - /// desired version of XCM). - BadLocation, - /// The referenced subscription could not be found. - NoSubscription, - /// The location is invalid since it already has a subscription from us. - AlreadySubscribed, - /// Could not check-out the assets for teleportation to the destination chain. - CannotCheckOutTeleport, - /// The owner does not own (all) of the asset that they wish to do the operation on. - LowBalance, - /// The asset owner has too many locks on the asset. - TooManyLocks, - /// The given account is not an identifiable sovereign account for any location. - AccountNotSovereign, - /// The operation required fees to be paid which the initiator could not meet. - FeesNotMet, - /// A remote lock with the corresponding data could not be found. - LockNotFound, - /// The unlock operation cannot succeed because there are still consumers of the lock. - InUse, - /// Invalid non-concrete asset. - InvalidAssetNotConcrete, - /// Invalid asset, reserve chain could not be determined for it. - InvalidAssetUnknownReserve, - /// Invalid asset, do not support remote asset reserves with different fees reserves. - InvalidAssetUnsupportedReserve, - /// Too many assets with different reserve locations have been attempted for transfer. - TooManyReserves, - /// Local XCM execution incomplete. - LocalExecutionIncomplete, -} - -impl TryFrom for Error { - type Error = PopApiError; - - fn try_from(status_code: u32) -> core::result::Result { - use Error::*; - match status_code { - 0 => Ok(Unreachable), - 1 => Ok(SendFailure), - 2 => Ok(Filtered), - 3 => Ok(UnweighableMessage), - 4 => Ok(DestinationNotInvertible), - 5 => Ok(Empty), - 6 => Ok(CannotReanchor), - 7 => Ok(TooManyAssets), - 8 => Ok(InvalidOrigin), - 9 => Ok(BadVersion), - 10 => Ok(BadLocation), - 11 => Ok(NoSubscription), - 12 => Ok(AlreadySubscribed), - 13 => Ok(CannotCheckOutTeleport), - 14 => Ok(LowBalance), - 15 => Ok(TooManyLocks), - 16 => Ok(AccountNotSovereign), - 17 => Ok(FeesNotMet), - 18 => Ok(LockNotFound), - 19 => Ok(InUse), - 20 => Ok(InvalidAssetNotConcrete), - 21 => Ok(InvalidAssetUnknownReserve), - 22 => Ok(InvalidAssetUnsupportedReserve), - 23 => Ok(TooManyReserves), - _ => Err(UnknownStatusCode(status_code)), - } - } -} - -impl From for Error { - fn from(error: PopApiError) -> Self { - match error { - PopApiError::Xcm(e) => e, - _ => panic!("expected xcm error"), - } - } -} diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index f7dab6b4..1c3642e1 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -2,12 +2,6 @@ use crate::{primitives::error::Error, StatusCode}; #[cfg(feature = "assets")] pub mod assets; -#[cfg(feature = "balances")] -pub mod balances; -#[cfg(feature = "cross-chain")] -pub mod cross_chain; -#[cfg(feature = "nfts")] -pub mod nfts; pub(crate) const V0: u8 = 0; diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs deleted file mode 100644 index 63b90a1f..00000000 --- a/pop-api/src/v0/nfts.rs +++ /dev/null @@ -1,883 +0,0 @@ -use super::RuntimeCall; -use crate::{PopApiError::UnknownStatusCode, *}; -use ink::prelude::vec::Vec; -use primitives::{ApprovalsLimit, BoundedBTreeMap, KeyLimit, MultiAddress}; -pub use primitives::{CollectionId, ItemId}; -use scale::Encode; -pub use types::*; - -type StringLimit = u32; -type MaxTips = u32; - -/// Issue a new collection of non-fungible items -pub fn create( - admin: impl Into>, - config: CollectionConfig, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Create { admin: admin.into(), config }))?) -} - -/// Destroy a collection of fungible items. -pub fn destroy(collection: CollectionId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Destroy { collection }))?) -} - -/// Mint an item of a particular collection. -pub fn mint( - collection: CollectionId, - item: ItemId, - mint_to: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Mint { - collection, - item, - mint_to: mint_to.into(), - witness_data: None, - }))?) -} - -/// Destroy a single item. -pub fn burn(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Burn { collection, item }))?) -} - -/// Move an item from the sender account to another. -pub fn transfer( - collection: CollectionId, - item: ItemId, - dest: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Transfer { collection, item, dest: dest.into() }))?) -} - -/// Re-evaluate the deposits on some items. -pub fn redeposit(collection: CollectionId, items: Vec) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Redeposit { collection, items }))?) -} - -/// Change the Owner of a collection. -pub fn transfer_ownership( - collection: CollectionId, - new_owner: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::TransferOwnership { - collection, - new_owner: new_owner.into(), - }))?) -} - -/// Set (or reset) the acceptance of ownership for a particular account. -pub fn set_accept_ownership( - collection: CollectionId, - maybe_collection: Option, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAcceptOwnership { collection, maybe_collection }))?) -} - -/// Set the maximum number of items a collection could have. -pub fn set_collection_max_supply(collection: CollectionId, max_supply: u32) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMaxSupply { collection, max_supply }))?) -} - -/// Update mint settings. -pub fn update_mint_settings(collection: CollectionId, mint_settings: MintSettings) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::UpdateMintSettings { collection, mint_settings }))?) -} - -/// Get the owner of the item, if the item exists. -pub fn owner(collection: CollectionId, item: ItemId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Owner(collection, item)))?) -} - -/// Get the owner of the collection, if the collection exists. -pub fn collection_owner(collection: CollectionId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionOwner(collection)))?) -} - -/// Get the details of a collection. -pub fn collection(collection: CollectionId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Collection(collection)))?) -} - -/// Get the details of an item. -pub fn item(collection: CollectionId, item: ItemId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Item(collection, item)))?) -} - -pub mod approvals { - use super::*; - - /// Approve an item to be transferred by a delegated third-party account. - pub fn approve_transfer( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - maybe_deadline: Option, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveTransfer { - collection, - item, - delegate: delegate.into(), - maybe_deadline, - }))?) - } - - /// Cancel one of the transfer approvals for a specific item. - pub fn cancel_approval( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelApproval { - collection, - item, - delegate: delegate.into(), - }))?) - } - - /// Cancel all the approvals of a specific item. - pub fn clear_all_transfer_approvals(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAllTransferApprovals { collection, item }))?) - } -} - -pub mod attributes { - use super::*; - - /// Approve item's attributes to be changed by a delegated third-party account. - pub fn approve_item_attribute( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveItemAttributes { - collection, - item, - delegate: delegate.into(), - }))?) - } - - /// Cancel the previously provided approval to change item's attributes. - pub fn cancel_item_attributes_approval( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelItemAttributesApproval { - collection, - item, - delegate: delegate.into(), - }))?) - } - - /// Set an attribute for a collection or item. - pub fn set_attribute( - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - value: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAttribute { - collection, - maybe_item, - namespace, - key, - value, - }))?) - } - - /// Clear an attribute for a collection or item. - pub fn clear_attribute( - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAttribute { - collection, - maybe_item, - namespace, - key, - }))?) - } - - /// Get the attribute value of `item` of `collection` corresponding to `key`. - pub fn attribute( - collection: CollectionId, - item: ItemId, - key: BoundedVec, - ) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Attribute(collection, item, key)))?) - } - - // /// Get the custom attribute value of `item` of `collection` corresponding to `key`. - // pub fn custom_attribute( - // account: AccountId, - // collection: CollectionId, - // item: ItemId, - // key: BoundedVec, - // ) -> Result>> { - // Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CustomAttribute( - // account, collection, item, key, - // )))?) - // } - - /// Get the system attribute value of `item` of `collection` corresponding to `key` if - /// `item` is `Some`. Otherwise, returns the system attribute value of `collection` - /// corresponding to `key`. - pub fn system_attribute( - collection: CollectionId, - item: Option, - key: BoundedVec, - ) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::SystemAttribute(collection, item, key)))?) - } - - /// Get the attribute value of `item` of `collection` corresponding to `key`. - pub fn collection_attribute( - collection: CollectionId, - key: BoundedVec, - ) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionAttribute(collection, key)))?) - } -} - -pub mod locking { - use super::*; - - /// Disallows changing the metadata or attributes of the item. - pub fn lock_item_properties( - collection: CollectionId, - item: ItemId, - lock_metadata: bool, - lock_attributes: bool, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemProperties { - collection, - item, - lock_metadata, - lock_attributes, - }))?) - } - - /// Disallow further unprivileged transfer of an item. - pub fn lock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemTransfer { collection, item }))?) - } - - /// Re-allow unprivileged transfer of an item. - pub fn unlock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::UnlockItemTransfer { collection, item }))?) - } - - /// Disallows specified settings for the whole collection. - pub fn lock_collection( - collection: CollectionId, - lock_settings: CollectionSettings, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockCollection { collection, lock_settings }))?) - } -} - -pub mod metadata { - use super::*; - - /// Set the metadata for an item. - pub fn set_metadata( - collection: CollectionId, - item: ItemId, - data: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetMetadata { collection, item, data }))?) - } - - /// Clear the metadata for an item. - pub fn clear_metadata(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearMetadata { collection, item }))?) - } - - /// Set the metadata for a collection. - pub fn set_collection_metadata( - collection: CollectionId, - data: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMetadata { collection, data }))?) - } - - /// Clear the metadata for a collection. - pub fn clear_collection_metadata(collection: CollectionId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearCollectionMetadata { collection }))?) - } -} - -pub mod roles { - use super::*; - - /// Change the Issuer, Admin and Freezer of a collection. - pub fn set_team( - collection: CollectionId, - issuer: Option>>, - admin: Option>>, - freezer: Option>>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetTeam { - collection, - issuer: issuer.map(|i| i.into()), - admin: admin.map(|i| i.into()), - freezer: freezer.map(|i| i.into()), - }))?) - } -} - -pub mod trading { - use super::*; - - /// Allows to pay the tips. - pub fn pay_tips(tips: BoundedVec) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::PayTips { tips }))?) - } - - /// Set (or reset) the price for an item. - pub fn price(collection: CollectionId, item: ItemId, price: Option) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetPrice { collection, item, price }))?) - } - - /// Allows to buy an item if it's up for sale. - pub fn buy_item(collection: CollectionId, item: ItemId, bid_price: Balance) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::BuyItem { collection, item, bid_price }))?) - } - - pub mod swaps { - use super::*; - - /// Register a new atomic swap, declaring an intention to send an `item` in exchange for - /// `desired_item` from origin to target on the current chain. - pub fn create_swap( - offered_collection: CollectionId, - offered_item: ItemId, - desired_collection: CollectionId, - maybe_desired_item: Option, - maybe_price: Option, - duration: BlockNumber, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CreateSwap { - offered_collection, - offered_item, - desired_collection, - maybe_desired_item, - maybe_price, - duration, - }))?) - } - - /// Cancel an atomic swap. - pub fn cancel_swap(offered_collection: CollectionId, offered_item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelSwap { - offered_collection, - offered_item, - }))?) - } - - /// Claim an atomic swap. - pub fn claim_swap( - send_collection: CollectionId, - send_item: ItemId, - receive_collection: CollectionId, - receive_item: ItemId, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClaimSwap { - send_collection, - send_item, - receive_collection, - receive_item, - }))?) - } - } -} - -#[derive(Encode)] -pub(crate) enum NftCalls { - #[codec(index = 0)] - Create { admin: MultiAddress, config: CollectionConfig }, - #[codec(index = 2)] - Destroy { collection: CollectionId }, - #[codec(index = 3)] - Mint { - collection: CollectionId, - item: ItemId, - mint_to: MultiAddress, - witness_data: Option<()>, - }, - #[codec(index = 5)] - Burn { collection: CollectionId, item: ItemId }, - #[codec(index = 6)] - Transfer { collection: CollectionId, item: ItemId, dest: MultiAddress }, - #[codec(index = 7)] - Redeposit { collection: CollectionId, items: Vec }, - #[codec(index = 8)] - LockItemTransfer { collection: CollectionId, item: ItemId }, - #[codec(index = 9)] - UnlockItemTransfer { collection: CollectionId, item: ItemId }, - #[codec(index = 10)] - LockCollection { collection: CollectionId, lock_settings: CollectionSettings }, - #[codec(index = 11)] - TransferOwnership { collection: CollectionId, new_owner: MultiAddress }, - #[codec(index = 12)] - SetTeam { - collection: CollectionId, - issuer: Option>, - admin: Option>, - freezer: Option>, - }, - #[codec(index = 15)] - ApproveTransfer { - collection: CollectionId, - item: ItemId, - delegate: MultiAddress, - maybe_deadline: Option, - }, - #[codec(index = 16)] - CancelApproval { collection: CollectionId, item: ItemId, delegate: MultiAddress }, - #[codec(index = 17)] - ClearAllTransferApprovals { collection: CollectionId, item: ItemId }, - #[codec(index = 18)] - LockItemProperties { - collection: CollectionId, - item: ItemId, - lock_metadata: bool, - lock_attributes: bool, - }, - #[codec(index = 19)] - SetAttribute { - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - value: BoundedVec, - }, - #[codec(index = 21)] - ClearAttribute { - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - }, - #[codec(index = 22)] - ApproveItemAttributes { - collection: CollectionId, - item: ItemId, - delegate: MultiAddress, - }, - #[codec(index = 23)] - CancelItemAttributesApproval { - collection: CollectionId, - item: ItemId, - delegate: MultiAddress, - }, - #[codec(index = 24)] - SetMetadata { collection: CollectionId, item: ItemId, data: BoundedVec }, - #[codec(index = 25)] - ClearMetadata { collection: CollectionId, item: ItemId }, - #[codec(index = 26)] - SetCollectionMetadata { collection: CollectionId, data: BoundedVec }, - #[codec(index = 27)] - ClearCollectionMetadata { collection: CollectionId }, - #[codec(index = 28)] - SetAcceptOwnership { collection: CollectionId, maybe_collection: Option }, - #[codec(index = 29)] - SetCollectionMaxSupply { collection: CollectionId, max_supply: u32 }, - #[codec(index = 30)] - UpdateMintSettings { collection: CollectionId, mint_settings: MintSettings }, - #[codec(index = 31)] - SetPrice { collection: CollectionId, item: ItemId, price: Option }, - #[codec(index = 32)] - BuyItem { collection: CollectionId, item: ItemId, bid_price: Balance }, - #[codec(index = 33)] - PayTips { tips: BoundedVec }, - #[codec(index = 34)] - CreateSwap { - offered_collection: CollectionId, - offered_item: ItemId, - desired_collection: CollectionId, - maybe_desired_item: Option, - maybe_price: Option, - duration: BlockNumber, - }, - #[codec(index = 35)] - CancelSwap { offered_collection: CollectionId, offered_item: ItemId }, - #[codec(index = 36)] - ClaimSwap { - send_collection: CollectionId, - send_item: ItemId, - receive_collection: CollectionId, - receive_item: ItemId, - }, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// The signing account has no permission to do the operation. - NoPermission, - /// The given item ID is unknown. - UnknownCollection, - /// The item ID has already been used for an item. - AlreadyExists, - /// The approval had a deadline that expired, so the approval isn't valid anymore. - ApprovalExpired, - /// The owner turned out to be different to what was expected. - WrongOwner, - /// The witness data given does not match the current state of the chain. - BadWitness, - /// Collection ID is already taken. - CollectionIdInUse, - /// Items within that collection are non-transferable. - ItemsNonTransferable, - /// The provided account is not a delegate. - NotDelegate, - /// The delegate turned out to be different to what was expected. - WrongDelegate, - /// No approval exists that would allow the transfer. - Unapproved, - /// The named owner has not signed ownership acceptance of the collection. - Unaccepted, - /// The item is locked (non-transferable). - ItemLocked, - /// Item's attributes are locked. - LockedItemAttributes, - /// Collection's attributes are locked. - LockedCollectionAttributes, - /// Item's metadata is locked. - LockedItemMetadata, - /// Collection's metadata is locked. - LockedCollectionMetadata, - /// All items have been minted. - MaxSupplyReached, - /// The max supply is locked and can't be changed. - MaxSupplyLocked, - /// The provided max supply is less than the number of items a collection already has. - MaxSupplyTooSmall, - /// The given item ID is unknown. - UnknownItem, - /// Swap doesn't exist. - UnknownSwap, - /// The given item has no metadata set. - MetadataNotFound, - /// The provided attribute can't be found. - AttributeNotFound, - /// Item is not for sale. - NotForSale, - /// The provided bid is too low. - BidTooLow, - /// The item has reached its approval limit. - ReachedApprovalLimit, - /// The deadline has already expired. - DeadlineExpired, - /// The duration provided should be less than or equal to `MaxDeadlineDuration`. - WrongDuration, - /// The method is disabled by system settings. - MethodDisabled, - /// The provided setting can't be set. - WrongSetting, - /// Item's config already exists and should be equal to the provided one. - InconsistentItemConfig, - /// Config for a collection or an item can't be found. - NoConfig, - /// Some roles were not cleared. - RolesNotCleared, - /// Mint has not started yet. - MintNotStarted, - /// Mint has already ended. - MintEnded, - /// The provided Item was already used for claiming. - AlreadyClaimed, - /// The provided data is incorrect. - IncorrectData, - /// The extrinsic was sent by the wrong origin. - WrongOrigin, - /// The provided signature is incorrect. - WrongSignature, - /// The provided metadata might be too long. - IncorrectMetadata, - /// Can't set more attributes per one call. - MaxAttributesLimitReached, - /// The provided namespace isn't supported in this call. - WrongNamespace, - /// Can't delete non-empty collections. - CollectionNotEmpty, - /// The witness data should be provided. - WitnessRequired, -} - -impl TryFrom for Error { - type Error = PopApiError; - - fn try_from(status_code: u32) -> core::result::Result { - use Error::*; - match status_code { - 0 => Ok(NoPermission), - 1 => Ok(UnknownCollection), - 2 => Ok(AlreadyExists), - 3 => Ok(ApprovalExpired), - 4 => Ok(WrongOwner), - 5 => Ok(BadWitness), - 6 => Ok(CollectionIdInUse), - 7 => Ok(ItemsNonTransferable), - 8 => Ok(NotDelegate), - 9 => Ok(WrongDelegate), - 10 => Ok(Unapproved), - 11 => Ok(Unaccepted), - 12 => Ok(ItemLocked), - 13 => Ok(LockedItemAttributes), - 14 => Ok(LockedCollectionAttributes), - 15 => Ok(LockedItemMetadata), - 16 => Ok(LockedCollectionMetadata), - 17 => Ok(MaxSupplyReached), - 18 => Ok(MaxSupplyLocked), - 19 => Ok(MaxSupplyTooSmall), - 20 => Ok(UnknownItem), - 21 => Ok(UnknownSwap), - 22 => Ok(MetadataNotFound), - 23 => Ok(AttributeNotFound), - 24 => Ok(NotForSale), - 25 => Ok(BidTooLow), - 26 => Ok(ReachedApprovalLimit), - 27 => Ok(DeadlineExpired), - 28 => Ok(WrongDuration), - 29 => Ok(MethodDisabled), - 30 => Ok(WrongSetting), - 31 => Ok(InconsistentItemConfig), - 32 => Ok(NoConfig), - 33 => Ok(RolesNotCleared), - 34 => Ok(MintNotStarted), - 35 => Ok(MintEnded), - 36 => Ok(AlreadyClaimed), - 37 => Ok(IncorrectData), - 38 => Ok(WrongOrigin), - 39 => Ok(WrongSignature), - 40 => Ok(IncorrectMetadata), - 41 => Ok(MaxAttributesLimitReached), - 42 => Ok(WrongNamespace), - 43 => Ok(CollectionNotEmpty), - 44 => Ok(WitnessRequired), - _ => Err(UnknownStatusCode(status_code)), - } - } -} - -impl From for Error { - fn from(error: PopApiError) -> Self { - match error { - PopApiError::Nfts(e) => e, - _ => panic!("expected nfts error"), - } - } -} - -// Local implementations of pallet-nfts types -mod types { - use super::*; - use crate::{ - primitives::{CollectionId, ItemId}, - Balance, BlockNumber, - }; - pub use enumflags2::{bitflags, BitFlags}; - use scale::{Decode, EncodeLike, MaxEncodedLen}; - use scale_info::{build::Fields, meta_type, prelude::vec, Path, Type, TypeInfo, TypeParameter}; - - /// Attribute namespaces for non-fungible tokens. - #[derive(Encode)] - pub enum AttributeNamespace { - /// An attribute was set by the pallet. - Pallet, - /// An attribute was set by collection's owner. - CollectionOwner, - /// An attribute was set by item's owner. - ItemOwner, - /// An attribute was set by pre-approved account. - Account(AccountId), - } - - /// Collection's configuration. - #[derive(Encode)] - pub struct CollectionConfig { - /// Collection's settings. - pub settings: CollectionSettings, - /// Collection's max supply. - pub max_supply: Option, - /// Default settings each item will get during the mint. - pub mint_settings: MintSettings, - } - - /// Information about a collection. - #[derive(Decode, Debug, Encode, Eq, PartialEq)] - pub struct CollectionDetails { - /// Collection's owner. - pub owner: AccountId, - /// The total balance deposited by the owner for all the storage data associated with this - /// collection. Used by `destroy`. - pub owner_deposit: Balance, - /// The total number of outstanding items of this collection. - pub items: u32, - /// The total number of outstanding item metadata of this collection. - pub item_metadatas: u32, - /// The total number of outstanding item configs of this collection. - pub item_configs: u32, - /// The total number of attributes for this collection. - pub attributes: u32, - } - - /// Wrapper type for `BitFlags` that implements `Codec`. - pub struct CollectionSettings(pub BitFlags); - - impl_codec_bitflags!(CollectionSettings, u64, CollectionSetting); - - /// Support for up to 64 user-enabled features on a collection. - #[bitflags] - #[repr(u64)] - #[derive(Copy, Clone, Encode, TypeInfo)] - pub enum CollectionSetting { - /// Items in this collection are transferable. - TransferableItems, - /// The metadata of this collection can be modified. - UnlockedMetadata, - /// Attributes of this collection can be modified. - UnlockedAttributes, - /// The supply of this collection can be modified. - UnlockedMaxSupply, - /// When this isn't set then the deposit is required to hold the items of this collection. - DepositRequired, - } - - /// Information concerning the ownership of a single unique item. - #[derive(Decode, Debug, Encode, Eq, PartialEq)] - pub struct ItemDetails { - /// The owner of this item. - pub owner: AccountId, - /// The approved transferrer of this item, if one is set. - pub approvals: BoundedBTreeMap, ApprovalsLimit>, - /// The amount held in the pallet's default account for this item. Free-hold items will - /// have this as zero. - pub deposit: Balance, - } - - /// Support for up to 64 user-enabled features on an item. - #[bitflags] - #[repr(u64)] - #[derive(Copy, Clone, Encode, TypeInfo)] - pub enum ItemSetting { - /// This item is transferable. - Transferable, - /// The metadata of this item can be modified. - UnlockedMetadata, - /// Attributes of this item can be modified. - UnlockedAttributes, - } - - /// Wrapper type for `BitFlags` that implements `Codec`. - pub struct ItemSettings(pub BitFlags); - - impl_codec_bitflags!(ItemSettings, u64, ItemSetting); - - /// Information about the tip. - #[derive(Encode)] - pub struct ItemTip { - /// The collection of the item. - pub(super) collection: CollectionId, - /// An item of which the tip is sent for. - pub(super) item: ItemId, - /// A sender of the tip. - pub(super) receiver: AccountId, - /// An amount the sender is willing to tip. - pub(super) amount: Balance, - } - - /// Holds the information about minting. - #[derive(Encode)] - pub struct MintSettings { - /// Whether anyone can mint or if minters are restricted to some subset. - pub mint_type: MintType, - /// An optional price per mint. - pub price: Option, - /// When the mint starts. - pub start_block: Option, - /// When the mint ends. - pub end_block: Option, - /// Default settings each item will get during the mint. - pub default_item_settings: ItemSettings, - } - - /// Mint type. Can the NFT be created by anyone, or only the creator of the collection, - /// or only by wallets that already hold an NFT from a certain collection? - /// The ownership of a privately minted NFT is still publicly visible. - #[derive(Encode)] - pub enum MintType { - /// Only an `Issuer` could mint items. - Issuer, - /// Anyone could mint items. - Public, - /// Only holders of items in specified collection could mint new items. - HolderOf(CollectionId), - } - - /// Holds the details about the price. - #[derive(Encode)] - pub struct PriceWithDirection { - /// An amount. - pub(super) amount: Balance, - /// A direction (send or receive). - pub(super) direction: PriceDirection, - } - - /// Specifies whether the tokens will be sent or received. - #[derive(Encode)] - pub enum PriceDirection { - /// Tokens will be sent. - Send, - /// Tokens will be received. - Receive, - } - - macro_rules! impl_codec_bitflags { - ($wrapper:ty, $size:ty, $bitflag_enum:ty) => { - impl MaxEncodedLen for $wrapper { - fn max_encoded_len() -> usize { - <$size>::max_encoded_len() - } - } - impl Encode for $wrapper { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } - } - impl EncodeLike for $wrapper {} - impl Decode for $wrapper { - fn decode( - input: &mut I, - ) -> core::result::Result { - let field = <$size>::decode(input)?; - Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) - } - } - - impl TypeInfo for $wrapper { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new( - "T", - Some(meta_type::<$bitflag_enum>()), - )]) - .composite( - Fields::unnamed() - .field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))), - ) - } - } - }; - } - pub(crate) use impl_codec_bitflags; -} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 5cbd6d6c..e7d55ffe 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -1,21 +1,17 @@ [package] name = "pop-primitives" +description = "Primitives crate for Pop" license = "GPL-3.0-only" version = "0.0.0" edition = "2021" [dependencies] -bounded-collections = { version = "0.1", default-features = false } -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2.10", default-features = false, features = ["derive"], optional = true } +codec.workspace = true +scale-info.workspace = true [features] default = ["std"] std = [ - "bounded-collections/std", - "scale/std", + "codec/std", "scale-info/std", -] -assets = [] -cross-chain = [] -nfts = [] +] \ No newline at end of file diff --git a/primitives/README.md b/primitives/README.md new file mode 100644 index 00000000..ded7918a --- /dev/null +++ b/primitives/README.md @@ -0,0 +1 @@ +Reserved crate for pop-primitives. \ No newline at end of file diff --git a/primitives/src/cross_chain.rs b/primitives/src/cross_chain.rs deleted file mode 100644 index 381e6a61..00000000 --- a/primitives/src/cross_chain.rs +++ /dev/null @@ -1,19 +0,0 @@ -use scale::{Decode, Encode, MaxEncodedLen}; - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum CrossChainMessage { - Relay(RelayChainMessage), -} - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum RelayChainMessage { - // Rococo index: https://github.com/paritytech/polkadot-sdk/blob/629506ce061db76d31d4f7a81f4a497752b27259/polkadot/runtime/rococo/src/lib.rs#L1423 - #[codec(index = 66)] - OnDemand(OnDemand), -} - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum OnDemand { - #[codec(index = 1)] - PlaceOrderKeepAlive { max_amount: u128, para_id: u32 }, -} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 376b440d..a51661ea 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,37 +1,13 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -pub use bounded_collections::{BoundedBTreeMap, BoundedBTreeSet, BoundedVec}; -use scale::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; #[cfg(feature = "std")] use scale_info::TypeInfo; pub use v0::error; -#[cfg(feature = "cross-chain")] -pub mod cross_chain; -pub mod storage_keys; - -/// An opaque 32-byte cryptographic identifier. -#[derive(Encode, Decode, Debug, MaxEncodedLen, Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(TypeInfo))] -pub struct AccountId(pub [u8; 32]); - /// Identifier for the class of asset. pub type AssetId = u32; -#[cfg(feature = "nfts")] -pub mod nfts { - use bounded_collections::ConstU32; - - /// Id used for identifying non-fungible collections. - pub type CollectionId = u32; - /// Id used for identifying non-fungible items. - pub type ItemId = u32; - /// The maximum length of an attribute key. - pub type KeyLimit = ConstU32<64>; - /// The maximum approvals an item could have. - pub type ApprovalsLimit = ConstU32<20>; -} - pub mod v0 { use super::*; pub mod error { @@ -102,7 +78,7 @@ pub mod v0 { } /// Description of what went wrong when trying to complete an operation on a token. - #[derive(Encode, Decode, Clone, Debug, MaxEncodedLen, Eq, PartialEq, Ord, PartialOrd)] + #[derive(Encode, Decode, Debug, Eq, PartialEq)] #[cfg_attr(feature = "std", derive(TypeInfo))] pub enum TokenError { /// Funds are unavailable. diff --git a/primitives/src/storage_keys.rs b/primitives/src/storage_keys.rs deleted file mode 100644 index e42dbca0..00000000 --- a/primitives/src/storage_keys.rs +++ /dev/null @@ -1,56 +0,0 @@ -#[cfg(feature = "nfts")] -use super::nfts::*; -use super::*; - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum RuntimeStateKeys { - #[cfg(feature = "cross-chain")] - #[codec(index = 1)] - ParachainSystem(ParachainSystemKeys), - #[cfg(feature = "nfts")] - #[codec(index = 50)] - Nfts(NftsKeys), - #[cfg(feature = "assets")] - #[codec(index = 52)] - Assets(AssetsKeys), -} - -#[cfg(feature = "cross-chain")] -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum ParachainSystemKeys { - /// Get the last relay chain block number seen by the parachain. - LastRelayChainBlockNumber, -} - -// https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/nfts/src/impl_nonfungibles.rs -#[cfg(feature = "nfts")] -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum NftsKeys { - // Get the details of a collection. - Collection(CollectionId), - /// Get the owner of the collection, if the collection exists. - CollectionOwner(CollectionId), - // Get the details of an item. - Item(CollectionId, ItemId), - /// Get the owner of the item, if the item exists. - Owner(CollectionId, ItemId), - /// Get the attribute value of `item` of `collection` corresponding to `key`. - Attribute(CollectionId, ItemId, BoundedVec), - /// Get the system attribute value of `item` of `collection` corresponding to `key` - SystemAttribute(CollectionId, Option, BoundedVec), - /// Get the attribute value of `item` of `collection` corresponding to `key`. - CollectionAttribute(CollectionId, BoundedVec), -} - -/// The required input for state queries in pallet assets. -#[cfg(feature = "assets")] -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum AssetsKeys { - TotalSupply(AssetId), - BalanceOf(AssetId, AccountId), - Allowance(AssetId, AccountId, AccountId), - TokenName(AssetId), - TokenSymbol(AssetId), - TokenDecimals(AssetId), - // AssetExists(AssetId), -} diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 70e6e9c9..455a86fd 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -22,8 +22,9 @@ scale-info.workspace = true smallvec.workspace = true # Local -pop-primitives = { workspace = true, features = ["assets", "cross-chain", "nfts"] } -pop-runtime-common = { workspace = true, default-features = false } +pop-primitives.workspace = true +pop-runtime-common.workspace = true +pallet-api.workspace = true # Substrate frame-benchmarking.workspace = true @@ -89,7 +90,6 @@ parachain-info.workspace = true [dev-dependencies] env_logger = "0.11.2" -enumflags2 = "0.7.9" hex = "0.4.3" rand = "0.8.5" @@ -119,6 +119,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-contracts/std", + "pallet-api/std", "pallet-message-queue/std", "pallet-multisig/std", "pallet-nft-fractionalization/std", @@ -169,6 +170,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-assets/runtime-benchmarks", + "pallet-api/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-contracts/runtime-benchmarks", diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs new file mode 100644 index 00000000..ae179e4a --- /dev/null +++ b/runtime/devnet/src/config/api.rs @@ -0,0 +1,34 @@ +use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::Contains; + +/// A query of runtime state. +#[derive(Encode, Decode, Debug, MaxEncodedLen)] +#[repr(u8)] +pub enum RuntimeRead { + /// Fungible token queries. + #[codec(index = 150)] + Fungibles(fungibles::Read), +} + +impl fungibles::Config for Runtime { + type AssetsInstance = TrustBackedAssetsInstance; + type WeightInfo = fungibles::weights::SubstrateWeight; +} + +/// A type to identify allowed calls to the Runtime from contracts. Used by Pop API +pub struct AllowedApiCalls; + +impl Contains for AllowedApiCalls { + fn contains(c: &RuntimeCall) -> bool { + use fungibles::Call as FungiblesCall; + matches!( + c, + RuntimeCall::Fungibles( + FungiblesCall::transfer { .. } + | FungiblesCall::approve { .. } + | FungiblesCall::increase_allowance { .. } + ) + ) + } +} diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 2c8ea952..78aed8b5 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -1,7 +1,3 @@ -use crate::{ - deposit, AccountId, Assets, Balance, Balances, BlockNumber, Nfts, Runtime, RuntimeEvent, - RuntimeHoldReason, DAYS, EXISTENTIAL_DEPOSIT, UNIT, -}; use frame_support::{ parameter_types, traits::{AsEnsureOriginWithArg, ConstU32}, @@ -12,6 +8,11 @@ use pallet_nfts::PalletFeatures; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId, Signature}; use sp_runtime::traits::Verify; +use crate::{ + deposit, AccountId, Assets, Balance, Balances, BlockNumber, Nfts, Runtime, RuntimeEvent, + RuntimeHoldReason, DAYS, EXISTENTIAL_DEPOSIT, UNIT, +}; + /// We allow root to execute privileged asset operations. pub type AssetsForceOrigin = EnsureRoot; diff --git a/runtime/devnet/src/config/mod.rs b/runtime/devnet/src/config/mod.rs index e0aaa3a1..f62ffa76 100644 --- a/runtime/devnet/src/config/mod.rs +++ b/runtime/devnet/src/config/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod api; pub mod assets; mod contracts; mod proxy; diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index d2bd63e7..3aed89df 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -1,61 +1,42 @@ -use codec::{Compact, Decode, Encode}; -use cumulus_pallet_parachain_system::RelaychainDataProvider; -use frame_support::traits::{Contains, OriginTrait}; +mod v0; + +use crate::{ + config::{ + api::{AllowedApiCalls, RuntimeRead}, + assets::TrustBackedAssetsInstance, + }, + fungibles::{ + self, + Read::{self, *}, + }, + AccountId, RuntimeCall, RuntimeOrigin, +}; +use codec::{Decode, Encode}; use frame_support::{ dispatch::{GetDispatchInfo, RawOrigin}, pallet_prelude::*, - traits::{ - fungibles::{approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect}, - nonfungibles_v2::Inspect as NonFungiblesInspect, - }, + traits::{Contains, OriginTrait}, }; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, }; +use pop_primitives::AssetId; use sp_core::crypto::UncheckedFrom; -use sp_runtime::{ - traits::{BlockNumberProvider, Dispatchable}, - DispatchError, MultiAddress, -}; -use sp_std::{boxed::Box, vec::Vec}; -use xcm::{ - latest::{prelude::*, OriginKind::SovereignAccount}, - VersionedXcm, -}; - -use crate::{ - config::assets::TrustBackedAssetsInstance, AccountId, AllowedApiCalls, Balance, Runtime, - RuntimeCall, RuntimeOrigin, UNIT, -}; -use pop_primitives::{ - cross_chain::CrossChainMessage, - nfts::{CollectionId, ItemId}, - storage_keys::{ - AssetsKeys::{self, *}, - NftsKeys, ParachainSystemKeys, RuntimeStateKeys, - }, - AssetId, -}; - -mod v0; +use sp_runtime::{traits::Dispatchable, DispatchError}; +use sp_std::vec::Vec; const LOG_TARGET: &str = "pop-api::extension"; -// Versions: -const V0: u8 = 0; type ContractSchedule = ::Schedule; #[derive(Default)] pub struct PopApiExtension; -// TODO: check removal or simplification of trait bounds. impl ChainExtension for PopApiExtension where T: pallet_contracts::Config - + pallet_xcm::Config + pallet_assets::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config + + fungibles::Config + frame_system::Config< RuntimeOrigin = RuntimeOrigin, AccountId = AccountId, @@ -102,8 +83,6 @@ where FuncId::ReadState => { read_state::(&mut env, version, pallet_index, call_index, params) }, - // TODO - FuncId::SendXcm => send_xcm::(&mut env), } }, Err(e) => Err(e), @@ -121,7 +100,7 @@ fn dispatch( version: u8, pallet_index: u8, call_index: u8, - params: Vec, + mut params: Vec, ) -> Result<(), DispatchError> where T: frame_system::Config, @@ -129,10 +108,19 @@ where E: Ext, { const LOG_PREFIX: &str = " dispatch |"; - let call = construct_call(version, pallet_index, call_index, params)?; + + // Prefix params with version, pallet, index to simplify decoding. + params.insert(0, version); + params.insert(1, pallet_index); + params.insert(2, call_index); + let call = ::decode(&mut ¶ms[..]) + .map_err(|_| DispatchError::Other("DecodingFailed"))?; + // Contract is the origin by default. let origin: RuntimeOrigin = RawOrigin::Signed(env.ext().address().clone()).into(); - dispatch_call::(env, call, origin, LOG_PREFIX) + match call { + VersionedDispatch::V0(call) => dispatch_call::(env, call, origin, LOG_PREFIX), + } } fn dispatch_call( @@ -165,79 +153,33 @@ where } } -fn construct_call( - version: u8, - pallet_index: u8, - call_index: u8, - params: Vec, -) -> Result { - match pallet_index { - index if index == super::Assets::index() as u8 => { - let call = versioned_construct_assets_call(version, call_index, params)?; - Ok(RuntimeCall::Assets(call)) - }, - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -fn construct_key( - version: u8, - pallet_index: u8, - call_index: u8, - params: Vec, -) -> Result { - match pallet_index { - 52 => { - let key = versioned_construct_assets_key(version, call_index, params)?; - Ok(RuntimeStateKeys::Assets(key)) - }, - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -fn versioned_construct_assets_call( - version: u8, - call_index: u8, - params: Vec, -) -> Result, DispatchError> { - match version { - V0 => v0::assets::construct_assets_call(call_index, params), - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -fn versioned_construct_assets_key( - version: u8, - call_index: u8, - params: Vec, -) -> Result { - match version { - V0 => v0::assets::construct_assets_key(call_index, params), - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - fn read_state( env: &mut Environment, version: u8, pallet_index: u8, call_index: u8, - params: Vec, + mut params: Vec, ) -> Result<(), DispatchError> where T: pallet_contracts::Config + pallet_assets::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config + + fungibles::Config + frame_system::Config, E: Ext, { const LOG_PREFIX: &str = " read_state |"; - let key = construct_key(version, pallet_index, call_index, params)?; + + // Prefix params with version, pallet, index to simplify decoding. + params.insert(0, version); + params.insert(1, pallet_index); + params.insert(2, call_index); + let key = >::decode(&mut ¶ms[..]) + .map_err(|_| DispatchError::Other("DecodingFailed"))?; + let result = match key { - RuntimeStateKeys::Nfts(key) => read_nfts_state::(key, env), - RuntimeStateKeys::ParachainSystem(key) => read_parachain_system_state::(key, env), - RuntimeStateKeys::Assets(key) => read_assets_state::(key, env), + VersionedStateRead::V0(key) => match key { + RuntimeRead::Fungibles(key) => read_fungibles_state::(key, env), + }, }? .encode(); log::trace!( @@ -247,48 +189,20 @@ where env.write(&result, false, None) } -fn send_xcm(env: &mut Environment) -> Result<(), DispatchError> -where - T: pallet_contracts::Config - + frame_system::Config< - RuntimeOrigin = RuntimeOrigin, - AccountId = AccountId, - RuntimeCall = RuntimeCall, - >, - E: Ext, -{ - const LOG_PREFIX: &str = " send_xcm |"; - // Read the input as CrossChainMessage. - let xc_call: CrossChainMessage = env.read_as::()?; - // Determine the call to dispatch. - let (dest, message) = match xc_call { - CrossChainMessage::Relay(message) => { - let dest = Location::parent().into_versioned(); - let assets: Asset = (Here, 10 * UNIT).into(); - let beneficiary: Location = - AccountId32 { id: (env.ext().address().clone()).into(), network: None }.into(); - let message = Xcm::builder() - .withdraw_asset(assets.clone().into()) - .buy_execution(assets.clone(), Unlimited) - .transact( - SovereignAccount, - Weight::from_parts(250_000_000, 10_000), - message.encode().into(), - ) - .refund_surplus() - .deposit_asset(assets.into(), beneficiary) - .build(); - (dest, message) - }, - }; - // TODO: revisit to replace with signed contract origin - let origin: RuntimeOrigin = RawOrigin::Root.into(); - // Generate runtime call to dispatch. - let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::send { - dest: Box::new(dest), - message: Box::new(VersionedXcm::V4(message)), - }); - dispatch_call::(env, call, origin, LOG_PREFIX) +/// Wrapper to enable versioning of runtime state reads. +#[derive(Decode, Debug)] +enum VersionedStateRead { + /// Version zero of state reads. + #[codec(index = 0)] + V0(RuntimeRead), +} + +/// Wrapper to enable versioning of runtime calls. +#[derive(Decode, Debug)] +enum VersionedDispatch { + /// Version zero of dispatch calls. + #[codec(index = 0)] + V0(RuntimeCall), } // Converts a `DispatchError` to a `u32` status code based on the version of the API the contract uses. @@ -331,7 +245,7 @@ pub(crate) fn convert_to_status_code(error: DispatchError, version: u8) -> u32 { // - Represents the second level of nesting in `DOUBLE_NESTED_ERRORS`. // - Byte 3: // - Unused or represents further nested information. - 0 => v0::error::handle_unknown_error(&mut encoded_error), + 0 => v0::handle_unknown_error(&mut encoded_error), _ => encoded_error = [254, 0, 0, 0], } u32::from_le_bytes(encoded_error) @@ -350,7 +264,6 @@ pub(crate) fn convert_to_status_code(error: DispatchError, version: u8) -> u32 { pub enum FuncId { Dispatch, ReadState, - SendXcm, } impl TryFrom for FuncId { @@ -364,7 +277,6 @@ impl TryFrom for FuncId { let id = match func_id { 0 => Self::Dispatch, 1 => Self::ReadState, - 2 => Self::SendXcm, _ => { return Err(DispatchError::Other("UnknownFuncId")); }, @@ -373,122 +285,27 @@ impl TryFrom for FuncId { } } -fn read_parachain_system_state( - key: ParachainSystemKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + cumulus_pallet_parachain_system::Config, - E: Ext, -{ - match key { - ParachainSystemKeys::LastRelayChainBlockNumber => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(RelaychainDataProvider::::current_block_number().encode()) - }, - } -} - -fn read_nfts_state( - key: NftsKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + pallet_nfts::Config, - E: Ext, -{ - match key { - NftsKeys::Collection(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Collection::::get(collection).encode()) - }, - NftsKeys::CollectionOwner(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_owner(collection).encode()) - }, - NftsKeys::Item(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Item::::get(collection, item).encode()) - }, - NftsKeys::Owner(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::owner(collection, item).encode()) - }, - NftsKeys::Attribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::attribute(&collection, &item, &key).encode()) - }, - // NftsKeys::CustomAttribute(account, collection, item, key) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key) - // .encode()) - // }, - NftsKeys::SystemAttribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::system_attribute(&collection, item.as_ref(), &key) - .encode()) - }, - NftsKeys::CollectionAttribute(collection, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_attribute(&collection, &key).encode()) - }, - } -} - -fn read_assets_state( - key: AssetsKeys, +fn read_fungibles_state( + key: Read, env: &mut Environment, ) -> Result, DispatchError> where T: pallet_contracts::Config - + pallet_assets::Config, + + pallet_assets::Config + + fungibles::Config, E: Ext, T: frame_system::Config, { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; match key { - TotalSupply(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_assets::Pallet::::total_supply(id).encode()) - }, - BalanceOf(id, owner) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_assets::Pallet::::balance(id, &owner.0.into()) - .encode()) - }, - Allowance(id, owner, spender) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_assets::Pallet::::allowance( - id, - &owner.0.into(), - &spender.0.into(), - ) - .encode()) - }, - TokenName(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok( as MetadataInspect< - AccountId, - >>::name(id) - .encode()) + TotalSupply(id) => Ok(fungibles::Pallet::::total_supply(id).encode()), + BalanceOf { id, owner } => Ok(fungibles::Pallet::::balance_of(id, &owner).encode()), + Allowance { id, owner, spender } => { + Ok(fungibles::Pallet::::allowance(id, &owner, &spender).encode()) }, - TokenSymbol(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok( as MetadataInspect< - AccountId, - >>::symbol(id) - .encode()) - }, - TokenDecimals(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok( as MetadataInspect< - AccountId, - >>::decimals(id) - .encode()) - }, - // AssetsKeys::AssetExists(id) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_assets::Pallet::::asset_exists(id).encode()) - // }, + TokenName(id) => Ok(fungibles::Pallet::::token_name(id).encode()), + TokenSymbol(id) => Ok(fungibles::Pallet::::token_symbol(id).encode()), + TokenDecimals(id) => Ok(fungibles::Pallet::::token_decimals(id).encode()), } } @@ -497,6 +314,7 @@ mod tests { use super::*; use crate::{Assets, Runtime, System}; use sp_runtime::BuildStorage; + // Test ensuring `func_id()` and `ext_id()` work as expected, i.e. extracting the first two // bytes and the last two bytes, respectively, from a 4 byte array. #[test] @@ -673,501 +491,3 @@ mod tests { }); } } -// use enumflags2::BitFlags; -// use pallet_nfts::{CollectionConfig, CollectionSetting, CollectionSettings, MintSettings}; -// use parachains_common::CollectionId; -// { -// // NFT helper functions -// fn collection_config_from_disabled_settings( -// settings: BitFlags, -// ) -> CollectionConfig { -// CollectionConfig { -// settings: CollectionSettings::from_disabled(settings), -// max_supply: None, -// mint_settings: MintSettings::default(), -// } -// } -// -// fn default_collection_config() -> CollectionConfig { -// collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()) -// } -// -// #[test] -// #[ignore] -// fn dispatch_balance_transfer_from_contract_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/balance-transfer/target/ink/balance_transfer.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("transfer_through_runtime"); -// let value_to_send: u128 = 10 * UNIT; -// let params = [function, BOB.encode(), value_to_send.encode()].concat(); -// -// let bob_balance_before = Balances::free_balance(&BOB); -// assert_eq!(bob_balance_before, INITIAL_AMOUNT); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// -// let bob_balance_after = Balances::free_balance(&BOB); -// assert_eq!(bob_balance_before + value_to_send, bob_balance_after); -// }); -// } -// -// // Create a test for tesing create_nft_collection -// #[test] -// #[ignore] -// fn dispatch_nfts_create_nft_collection() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/nfts/target/ink/pop_api_nft_example.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("create_nft_collection"); -// -// let params = [function].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check that the nft collection was created -// assert_eq!(Nfts::collection_owner(0), Some(addr.clone().into())); -// -// // test reading the collection -// let function = function_selector("read_collection"); -// -// let params = [function, 0.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // assert that the collection was read successfully -// assert_eq!(result.result.clone().unwrap().data, vec![1, 1]); -// }); -// } -// -// #[test] -// #[ignore] -// fn dispatch_nfts_mint_from_contract_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = -// load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") -// .unwrap(); -// -// let init_value = 100; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let collection_id: u32 = 0; -// let item_id: u32 = 1; -// -// // create nft collection with contract as owner -// assert_eq!( -// Nfts::force_create( -// RuntimeOrigin::root(), -// addr.clone().into(), -// default_collection_config() -// ), -// Ok(()) -// ); -// -// assert_eq!(Nfts::collection_owner(collection_id), Some(addr.clone().into())); -// // assert that the item does not exist yet -// assert_eq!(Nfts::owner(collection_id, item_id), None); -// -// let function = function_selector("mint_through_runtime"); -// -// let params = -// [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// -// assert_eq!(Nfts::owner(collection_id, item_id), Some(BOB.into())); -// }); -// } -// -// #[test] -// #[ignore] -// fn nfts_mint_surfaces_error() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = -// load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") -// .unwrap(); -// -// let init_value = 100; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let collection_id: u32 = 0; -// let item_id: u32 = 1; -// -// let function = function_selector("mint_through_runtime"); -// -// let params = -// [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert with expected error -// let result = result.result.unwrap(); -// assert!(result.did_revert()); -// }); -// } -// -// #[test] -// #[ignore] -// fn reading_last_relay_chain_block_number_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/read-runtime-state/target/ink/read_relay_blocknumber.wasm", -// ) -// .unwrap(); -// -// let init_value = 100; -// -// let contract = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!contract.result.did_revert(), "deploying contract reverted {:?}", contract); -// -// let addr = contract.account_id; -// -// let function = function_selector("read_relay_block_number"); -// let params = [function].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::UnsafeCollect, -// pallet_contracts::Determinism::Relaxed, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// }); -// } -// -// #[test] -// #[ignore] -// fn place_spot_order_from_contract_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/place-spot-order/target/ink/spot_order.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("place_spot_order"); -// -// let max_amount = 1 * UNIT; -// let para_id = 2000; -// -// let params = [function, max_amount.encode(), para_id.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// }); -// } -// -// #[test] -// #[ignore] -// fn allow_call_filter_blocks_call() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../tests/contracts/filtered-call/target/ink/pop_api_filtered_call.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("get_filtered"); -// let params = [function].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("filtered result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// }); -// } -// } diff --git a/runtime/devnet/src/extensions/v0/error.rs b/runtime/devnet/src/extensions/v0.rs similarity index 100% rename from runtime/devnet/src/extensions/v0/error.rs rename to runtime/devnet/src/extensions/v0.rs diff --git a/runtime/devnet/src/extensions/v0/assets.rs b/runtime/devnet/src/extensions/v0/assets.rs deleted file mode 100644 index c6b15b4e..00000000 --- a/runtime/devnet/src/extensions/v0/assets.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::extensions::{ - AccountId as AccountId32, AssetId, - AssetsKeys::{self, *}, - Balance, Compact, Decode, DispatchError, MultiAddress, Runtime, TrustBackedAssetsInstance, -}; -use pop_primitives::AccountId; -use sp_std::vec::Vec; - -pub(crate) fn construct_assets_key( - call_index: u8, - params: Vec, -) -> Result { - match call_index { - 0 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TotalSupply(id)) - }, - 1 => { - let (id, owner) = <(AssetId, AccountId)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(BalanceOf(id, owner)) - }, - 2 => { - let (id, owner, spender) = <(AssetId, AccountId, AccountId)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(Allowance(id, owner, spender)) - }, - 3 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenName(id)) - }, - 4 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenSymbol(id)) - }, - 5 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenDecimals(id)) - }, - // other calls - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -pub(crate) fn construct_assets_call( - call_index: u8, - params: Vec, -) -> Result, DispatchError> { - match call_index { - 9 => { - let (id, target, amount) = <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(pallet_assets::Call::::transfer_keep_alive { - id: Compact(id), - target: MultiAddress::Id(target), - amount, - }) - }, - 22 => { - let (id, delegate, amount) = - <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(pallet_assets::Call::::approve_transfer { - id: Compact(id), - delegate: MultiAddress::Id(delegate), - amount, - }) - }, - // other calls - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} diff --git a/runtime/devnet/src/extensions/v0/mod.rs b/runtime/devnet/src/extensions/v0/mod.rs deleted file mode 100644 index 6406e08f..00000000 --- a/runtime/devnet/src/extensions/v0/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod assets; -pub(crate) mod error; diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 9ab64043..23895310 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -73,6 +73,8 @@ use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; // XCM Imports use xcm::latest::prelude::BodyId; +pub(crate) use pallet_api::fungibles; + /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <::Signer as IdentifyAccount>::AccountId; @@ -250,82 +252,6 @@ impl Contains for FilteredCalls { } } -/// A type to identify allowed calls to the Runtime from contracts. Used by Pop API -pub struct AllowedApiCalls; -impl Contains for crate::AllowedApiCalls { - fn contains(c: &RuntimeCall) -> bool { - use config::assets::AssetsCall; - use pallet_nfts::Call as NftsCall; - matches!( - c, - RuntimeCall::Balances(BalancesCall::transfer_keep_alive { .. }) - | RuntimeCall::Assets( - AssetsCall::create { .. } - | AssetsCall::start_destroy { .. } - | AssetsCall::destroy_accounts { .. } - | AssetsCall::destroy_approvals { .. } - | AssetsCall::finish_destroy { .. } - | AssetsCall::mint { .. } - | AssetsCall::burn { .. } - | AssetsCall::transfer { .. } - | AssetsCall::transfer_keep_alive { .. } - | AssetsCall::force_transfer { .. } - | AssetsCall::freeze { .. } - | AssetsCall::thaw { .. } - | AssetsCall::freeze_asset { .. } - | AssetsCall::thaw_asset { .. } - | AssetsCall::transfer_ownership { .. } - | AssetsCall::set_team { .. } - | AssetsCall::set_metadata { .. } - | AssetsCall::clear_metadata { .. } - | AssetsCall::approve_transfer { .. } - | AssetsCall::cancel_approval { .. } - | AssetsCall::force_cancel_approval { .. } - | AssetsCall::transfer_approved { .. } - | AssetsCall::touch { .. } - | AssetsCall::refund { .. } - | AssetsCall::set_min_balance { .. } - | AssetsCall::touch_other { .. } - | AssetsCall::refund_other { .. } - | AssetsCall::block { .. } - ) | RuntimeCall::Nfts( - NftsCall::create { .. } - | NftsCall::destroy { .. } - | NftsCall::mint { .. } - | NftsCall::burn { .. } - | NftsCall::transfer { .. } - | NftsCall::redeposit { .. } - | NftsCall::lock_item_transfer { .. } - | NftsCall::unlock_item_transfer { .. } - | NftsCall::lock_collection { .. } - | NftsCall::transfer_ownership { .. } - | NftsCall::set_team { .. } - | NftsCall::approve_transfer { .. } - | NftsCall::cancel_approval { .. } - | NftsCall::clear_all_transfer_approvals { .. } - | NftsCall::lock_item_properties { .. } - | NftsCall::set_attribute { .. } - | NftsCall::clear_attribute { .. } - | NftsCall::approve_item_attributes { .. } - | NftsCall::cancel_item_attributes_approval { .. } - | NftsCall::set_metadata { .. } - | NftsCall::clear_metadata { .. } - | NftsCall::set_collection_metadata { .. } - | NftsCall::clear_collection_metadata { .. } - | NftsCall::set_accept_ownership { .. } - | NftsCall::set_collection_max_supply { .. } - | NftsCall::update_mint_settings { .. } - | NftsCall::set_price { .. } - | NftsCall::buy_item { .. } - | NftsCall::pay_tips { .. } - | NftsCall::create_swap { .. } - | NftsCall::cancel_swap { .. } - | NftsCall::claim_swap { .. } - ) - ) - } -} - /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from /// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), /// but overridden as needed. @@ -663,6 +589,9 @@ construct_runtime!( Nfts: pallet_nfts = 50, NftFractionalization: pallet_nft_fractionalization = 51, Assets: pallet_assets:: = 52, + + // Pop API + Fungibles: fungibles = 150, } ); @@ -670,6 +599,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [pallet_api::fungibles, Fungibles] [pallet_balances, Balances] [pallet_session, SessionBench::] [pallet_timestamp, Timestamp] @@ -1030,3 +960,22 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } + +#[cfg(test)] +mod tests { + use crate::Runtime; + use std::any::TypeId; + + // Ensures that the account id lookup does not perform any state reads. When this changes, + // `pallet_api::fungibles` dispatchables need to be re-evaluated. + #[test] + fn test_lookup_config() { + type ExpectedLookup = sp_runtime::traits::AccountIdLookup; + type ConfigLookup = ::Lookup; + + let expected_type_id = TypeId::of::(); + let config_type_id = TypeId::of::(); + + assert_eq!(config_type_id, expected_type_id); + } +} diff --git a/runtime/testnet/Cargo.toml b/runtime/testnet/Cargo.toml index b04c3102..51cab6d6 100644 --- a/runtime/testnet/Cargo.toml +++ b/runtime/testnet/Cargo.toml @@ -22,8 +22,8 @@ scale-info.workspace = true smallvec.workspace = true # Local -pop-primitives = { workspace = true, features = ["nfts", "cross-chain"] } -pop-runtime-common = { workspace = true, default-features = false } +pop-primitives.workspace = true +pop-runtime-common.workspace = true # Substrate frame-benchmarking.workspace = true @@ -90,7 +90,6 @@ parachain-info.workspace = true [dev-dependencies] env_logger = "0.11.2" hex = "0.4.3" -enumflags2 = "0.7.9" [features] default = ["std"] diff --git a/runtime/testnet/src/extensions.rs b/runtime/testnet/src/extensions.rs index bcb0f835..a6e309f9 100644 --- a/runtime/testnet/src/extensions.rs +++ b/runtime/testnet/src/extensions.rs @@ -1,22 +1,13 @@ -use cumulus_pallet_parachain_system::RelaychainDataProvider; +use frame_support::traits::{Contains, OriginTrait}; use frame_support::{ dispatch::{GetDispatchInfo, RawOrigin}, pallet_prelude::*, - traits::nonfungibles_v2::Inspect, - traits::{Contains, OriginTrait}, }; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, ChargedAmount, Environment, Ext, InitState, RetVal, }; -use pop_primitives::{ - nfts::{CollectionId, ItemId}, - storage_keys::{NftsKeys, ParachainSystemKeys, RuntimeStateKeys}, -}; use sp_core::crypto::UncheckedFrom; -use sp_runtime::{ - traits::{BlockNumberProvider, Dispatchable}, - DispatchError, -}; +use sp_runtime::{traits::Dispatchable, DispatchError}; use sp_std::vec::Vec; use crate::{AccountId, AllowedApiCalls, RuntimeCall, RuntimeOrigin}; @@ -31,9 +22,6 @@ pub struct PopApiExtension; impl ChainExtension for PopApiExtension where T: pallet_contracts::Config - + pallet_xcm::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config + frame_system::Config< RuntimeOrigin = RuntimeOrigin, AccountId = AccountId, @@ -44,7 +32,6 @@ where fn call(&mut self, env: Environment) -> Result where E: Ext, - T::AccountId: UncheckedFrom + AsRef<[u8]>, { log::debug!(target:LOG_TARGET, " extension called "); match v0::FuncId::try_from(env.func_id())? { @@ -101,8 +88,6 @@ fn dispatch_call( log_prefix: &str, ) -> Result<(), DispatchError> where - T: frame_system::Config, - RuntimeOrigin: From>, E: Ext, { let charged_dispatch_weight = env.charge_weight(call.get_dispatch_info().weight)?; @@ -157,8 +142,7 @@ where fn dispatch(env: Environment) -> Result<(), DispatchError> where - T: pallet_contracts::Config - + frame_system::Config, + T: pallet_contracts::Config, RuntimeOrigin: From>, E: Ext, { @@ -180,10 +164,7 @@ where fn read_state(env: Environment) -> Result<(), DispatchError> where - T: pallet_contracts::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config - + frame_system::Config, + T: pallet_contracts::Config, E: Ext, { const LOG_PREFIX: &str = " read_state |"; @@ -199,16 +180,9 @@ where log::debug!(target:LOG_TARGET, "{} charged weight: {:?}", LOG_PREFIX, charged_weight); - let key: RuntimeStateKeys = env.read_as()?; - - let result = match key { - RuntimeStateKeys::Nfts(key) => read_nfts_state::(key, &mut env), - RuntimeStateKeys::ParachainSystem(key) => { - read_parachain_system_state::(key, &mut env) - }, - _ => Ok(Vec::default()), - }? - .encode(); + // TODO: always returning an empty vec. Chainextension will be refactored into one for both + // runtimes before pop api implementation gets merged into main. + let result = Vec::::default().encode(); log::trace!( target:LOG_TARGET, @@ -219,521 +193,3 @@ where DispatchError::Other("unable to write results to contract memory") }) } - -fn read_parachain_system_state( - key: ParachainSystemKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + cumulus_pallet_parachain_system::Config, - E: Ext, -{ - match key { - ParachainSystemKeys::LastRelayChainBlockNumber => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(RelaychainDataProvider::::current_block_number().encode()) - }, - } -} - -fn read_nfts_state( - key: NftsKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + pallet_nfts::Config, - E: Ext, -{ - match key { - NftsKeys::Collection(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Collection::::get(collection).encode()) - }, - NftsKeys::CollectionOwner(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_owner(collection).encode()) - }, - NftsKeys::Item(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Item::::get(collection, item).encode()) - }, - NftsKeys::Owner(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::owner(collection, item).encode()) - }, - NftsKeys::Attribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::attribute(&collection, &item, &key).encode()) - }, - // NftsKeys::CustomAttribute(account, collection, item, key) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key) - // .encode()) - // }, - NftsKeys::SystemAttribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::system_attribute(&collection, item.as_ref(), &key) - .encode()) - }, - NftsKeys::CollectionAttribute(collection, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_attribute(&collection, &key).encode()) - }, - } -} - -#[cfg(test)] -mod tests { - pub use super::*; - pub use crate::*; - use enumflags2::BitFlags; - pub use pallet_contracts::Code; - use pallet_nfts::{CollectionConfig, CollectionSetting, CollectionSettings, MintSettings}; - use parachains_common::CollectionId; - pub use sp_runtime::{traits::Hash, AccountId32}; - - const DEBUG_OUTPUT: pallet_contracts::DebugInfo = pallet_contracts::DebugInfo::UnsafeDebug; - - const ALICE: AccountId32 = AccountId32::new([1_u8; 32]); - const BOB: AccountId32 = AccountId32::new([2_u8; 32]); - const INITIAL_AMOUNT: u128 = 100_000 * UNIT; - const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); - - fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .expect("Frame system builds valid default genesis config"); - - pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, INITIAL_AMOUNT), (BOB, INITIAL_AMOUNT)], - } - .assimilate_storage(&mut t) - .expect("Pallet balances storage can be assimilated"); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } - - fn load_wasm_module(path: &str) -> std::io::Result<(Vec, ::Output)> - where - T: frame_system::Config, - { - let wasm_binary = std::fs::read(path)?; - let code_hash = T::Hashing::hash(&wasm_binary); - Ok((wasm_binary, code_hash)) - } - - fn function_selector(name: &str) -> Vec { - let hash = sp_io::hashing::blake2_256(name.as_bytes()); - [hash[0..4].to_vec()].concat() - } - - // NFT helper functions - fn collection_config_from_disabled_settings( - settings: BitFlags, - ) -> CollectionConfig { - CollectionConfig { - settings: CollectionSettings::from_disabled(settings), - max_supply: None, - mint_settings: MintSettings::default(), - } - } - - fn default_collection_config() -> CollectionConfig { - collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()) - } - - #[test] - #[ignore] - fn dispatch_balance_transfer_from_contract_works() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../pop-api/examples/balance-transfer/target/ink/balance_transfer.wasm", - ) - .unwrap(); - - let init_value = 100 * UNIT; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let function = function_selector("transfer_through_runtime"); - let value_to_send: u128 = 10 * UNIT; - let params = [function, BOB.encode(), value_to_send.encode()].concat(); - - let bob_balance_before = Balances::free_balance(&BOB); - assert_eq!(bob_balance_before, INITIAL_AMOUNT); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - - let bob_balance_after = Balances::free_balance(&BOB); - assert_eq!(bob_balance_before + value_to_send, bob_balance_after); - }); - } - - #[test] - #[ignore] - fn dispatch_nfts_mint_from_contract_works() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = - load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") - .unwrap(); - - let init_value = 100; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let collection_id: u32 = 0; - let item_id: u32 = 1; - - // create nft collection with contract as owner - assert_eq!( - Nfts::force_create( - RuntimeOrigin::root(), - addr.clone().into(), - default_collection_config() - ), - Ok(()) - ); - - assert_eq!(Nfts::collection_owner(collection_id), Some(addr.clone().into())); - // assert that the item does not exist yet - assert_eq!(Nfts::owner(collection_id, item_id), None); - - let function = function_selector("mint_through_runtime"); - - let params = - [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - - assert_eq!(Nfts::owner(collection_id, item_id), Some(BOB.into())); - }); - } - - #[test] - #[ignore] - fn nfts_mint_surfaces_error() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = - load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") - .unwrap(); - - let init_value = 100; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let collection_id: u32 = 0; - let item_id: u32 = 1; - - let function = function_selector("mint_through_runtime"); - - let params = - [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert with expected error - let result = result.result.unwrap(); - assert!(result.did_revert()); - }); - } - - #[test] - #[ignore] - fn reading_last_relay_chain_block_number_works() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../pop-api/examples/read-runtime-state/target/ink/read_relay_blocknumber.wasm", - ) - .unwrap(); - - let init_value = 100; - - let contract = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!contract.result.did_revert(), "deploying contract reverted {:?}", contract); - - let addr = contract.account_id; - - let function = function_selector("read_relay_block_number"); - let params = [function].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::UnsafeCollect, - pallet_contracts::Determinism::Relaxed, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - }); - } - - #[test] - #[ignore] - fn place_spot_order_from_contract_fails() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../pop-api/examples/place-spot-order/target/ink/spot_order.wasm", - ) - .unwrap(); - - let init_value = 100 * UNIT; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let function = function_selector("place_spot_order"); - - let max_amount = 1 * UNIT; - let para_id = 2000; - - let params = [function, max_amount.encode(), para_id.encode()].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!( - result.result.is_err(), - "Contract execution should have failed - unimplemented runtime call!" - ); - }); - } - - #[test] - #[ignore] - fn allow_call_filter_blocks_call() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../tests/contracts/filtered-call/target/ink/pop_api_filtered_call.wasm", - ) - .unwrap(); - - let init_value = 100 * UNIT; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let function = function_selector("get_filtered"); - let params = [function].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("filtered result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - }); - } -} diff --git a/runtime/testnet/src/lib.rs b/runtime/testnet/src/lib.rs index c4178011..5573ef18 100644 --- a/runtime/testnet/src/lib.rs +++ b/runtime/testnet/src/lib.rs @@ -252,45 +252,8 @@ impl Contains for FilteredCalls { /// A type to identify allowed calls to the Runtime from contracts. Used by Pop API pub struct AllowedApiCalls; impl Contains for AllowedApiCalls { - fn contains(c: &RuntimeCall) -> bool { - use pallet_nfts::Call as NftsCall; - matches!( - c, - RuntimeCall::Balances(BalancesCall::transfer_keep_alive { .. }) - | RuntimeCall::Nfts( - NftsCall::create { .. } - | NftsCall::destroy { .. } - | NftsCall::mint { .. } | NftsCall::burn { .. } - | NftsCall::transfer { .. } - | NftsCall::redeposit { .. } - | NftsCall::lock_item_transfer { .. } - | NftsCall::unlock_item_transfer { .. } - | NftsCall::lock_collection { .. } - | NftsCall::transfer_ownership { .. } - | NftsCall::set_team { .. } - | NftsCall::approve_transfer { .. } - | NftsCall::cancel_approval { .. } - | NftsCall::clear_all_transfer_approvals { .. } - | NftsCall::lock_item_properties { .. } - | NftsCall::set_attribute { .. } - | NftsCall::clear_attribute { .. } - | NftsCall::approve_item_attributes { .. } - | NftsCall::cancel_item_attributes_approval { .. } - | NftsCall::set_metadata { .. } - | NftsCall::clear_metadata { .. } - | NftsCall::set_collection_metadata { .. } - | NftsCall::clear_collection_metadata { .. } - | NftsCall::set_accept_ownership { .. } - | NftsCall::set_collection_max_supply { .. } - | NftsCall::update_mint_settings { .. } - | NftsCall::set_price { .. } - | NftsCall::buy_item { .. } - | NftsCall::pay_tips { .. } - | NftsCall::create_swap { .. } - | NftsCall::cancel_swap { .. } - | NftsCall::claim_swap { .. } - ) - ) + fn contains(_c: &RuntimeCall) -> bool { + false } } diff --git a/scripts/pallet-weights-template.hbs b/scripts/pallet-weights-template.hbs new file mode 100644 index 00000000..9e1e5a46 --- /dev/null +++ b/scripts/pallet-weights-template.hbs @@ -0,0 +1,122 @@ +{{header}} +//! Autogenerated weights for `{{pallet}}` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `R0GUE`, CPU: `{{cpuname}}` +//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}` + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `{{pallet}}`. +pub trait WeightInfo { + {{#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{c.name}}: u32, {{/each~}} + ) -> Weight; + {{/each}} +} + +/// Weights for `{{pallet}}` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +{{#if (eq pallet "frame_system")}} +impl WeightInfo for SubstrateWeight { +{{else}} +impl WeightInfo for SubstrateWeight { +{{/if}} + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} +