From 5b7dd3d3576cf986900b18cef2811da6388e48be Mon Sep 17 00:00:00 2001
From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com>
Date: Wed, 27 Dec 2023 20:11:48 +0100
Subject: [PATCH] View-Only chain extension for `pallet-uniques` (#5)
* add chain extension
* add extension to local-runtime
* add extension to shibuya-runtime
* fix weight
* fix
---
Cargo.lock | 31 +++
Cargo.toml | 3 +
chain-extensions/pallet-uniques/Cargo.toml | 41 ++++
chain-extensions/pallet-uniques/src/lib.rs | 211 ++++++++++++++++++
.../pallet-uniques/src/weights.rs | 77 +++++++
chain-extensions/types/uniques/Cargo.toml | 25 +++
chain-extensions/types/uniques/src/lib.rs | 37 +++
runtime/local/Cargo.toml | 2 +
runtime/local/src/chain_extensions.rs | 7 +
runtime/local/src/lib.rs | 1 +
runtime/shibuya/Cargo.toml | 2 +
runtime/shibuya/src/chain_extensions.rs | 7 +
runtime/shibuya/src/lib.rs | 1 +
13 files changed, 445 insertions(+)
create mode 100644 chain-extensions/pallet-uniques/Cargo.toml
create mode 100644 chain-extensions/pallet-uniques/src/lib.rs
create mode 100644 chain-extensions/pallet-uniques/src/weights.rs
create mode 100644 chain-extensions/types/uniques/Cargo.toml
create mode 100644 chain-extensions/types/uniques/src/lib.rs
diff --git a/Cargo.lock b/Cargo.lock
index 3bed5d4f35..113c9ebfbf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6030,6 +6030,7 @@ dependencies = [
"pallet-block-rewards-hybrid",
"pallet-chain-extension-assets",
"pallet-chain-extension-unified-accounts",
+ "pallet-chain-extension-uniques",
"pallet-chain-extension-xvm",
"pallet-collective",
"pallet-contracts",
@@ -7506,6 +7507,25 @@ dependencies = [
"unified-accounts-chain-extension-types",
]
+[[package]]
+name = "pallet-chain-extension-uniques"
+version = "0.1.0"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "log",
+ "num-traits",
+ "pallet-contracts",
+ "pallet-contracts-primitives",
+ "pallet-uniques",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-runtime",
+ "sp-std",
+ "uniques-chain-extension-types",
+]
+
[[package]]
name = "pallet-chain-extension-xvm"
version = "0.1.1"
@@ -13188,6 +13208,7 @@ dependencies = [
"pallet-balances",
"pallet-chain-extension-assets",
"pallet-chain-extension-unified-accounts",
+ "pallet-chain-extension-uniques",
"pallet-chain-extension-xvm",
"pallet-collator-selection",
"pallet-collective",
@@ -15397,6 +15418,16 @@ dependencies = [
"sp-runtime",
]
+[[package]]
+name = "uniques-chain-extension-types"
+version = "0.1.0"
+dependencies = [
+ "frame-system",
+ "pallet-contracts",
+ "parity-scale-codec",
+ "scale-info",
+]
+
[[package]]
name = "universal-hash"
version = "0.4.1"
diff --git a/Cargo.toml b/Cargo.toml
index 3e7f41a8c7..e6024cc266 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ members = [
"chain-extensions/pallet-assets",
"chain-extensions/xvm",
"chain-extensions/unified-accounts",
+ "chain-extensions/pallet-uniques",
"chain-extensions/types/*",
"vendor/evm-tracing",
@@ -301,10 +302,12 @@ pallet-evm-precompile-unified-accounts = { path = "./precompiles/unified-account
pallet-chain-extension-xvm = { path = "./chain-extensions/xvm", default-features = false }
pallet-chain-extension-assets = { path = "./chain-extensions/pallet-assets", default-features = false }
pallet-chain-extension-unified-accounts = { path = "./chain-extensions/unified-accounts", default-features = false }
+pallet-chain-extension-uniques = { path = "./chain-extensions/pallet-uniques", default-features = false }
xvm-chain-extension-types = { path = "./chain-extensions/types/xvm", default-features = false }
assets-chain-extension-types = { path = "./chain-extensions/types/assets", default-features = false }
unified-accounts-chain-extension-types = { path = "./chain-extensions/types/unified-accounts", default-features = false }
+uniques-chain-extension-types = { path = "./chain-extensions/types/uniques", default-features = false }
precompile-utils = { path = "./precompiles/utils", default-features = false }
diff --git a/chain-extensions/pallet-uniques/Cargo.toml b/chain-extensions/pallet-uniques/Cargo.toml
new file mode 100644
index 0000000000..f989e56adc
--- /dev/null
+++ b/chain-extensions/pallet-uniques/Cargo.toml
@@ -0,0 +1,41 @@
+[package]
+name = "pallet-chain-extension-uniques"
+version = "0.1.0"
+license = "Apache-2.0"
+description = "Uniques chain extension for WASM contracts"
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+uniques-chain-extension-types = { workspace = true }
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+log = { workspace = true }
+num-traits = { workspace = true }
+pallet-uniques = { workspace = true }
+pallet-contracts = { workspace = true }
+pallet-contracts-primitives = { workspace = true }
+parity-scale-codec = { workspace = true }
+scale-info = { workspace = true }
+sp-core = { workspace = true }
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+
+[features]
+default = ["std"]
+std = [
+ "parity-scale-codec/std",
+ "frame-support/std",
+ "frame-system/std",
+ "num-traits/std",
+ "pallet-contracts/std",
+ "pallet-contracts-primitives/std",
+ "scale-info/std",
+ "sp-std/std",
+ "sp-core/std",
+ "sp-runtime/std",
+ "pallet-uniques/std",
+ "uniques-chain-extension-types/std",
+]
diff --git a/chain-extensions/pallet-uniques/src/lib.rs b/chain-extensions/pallet-uniques/src/lib.rs
new file mode 100644
index 0000000000..ebf285853b
--- /dev/null
+++ b/chain-extensions/pallet-uniques/src/lib.rs
@@ -0,0 +1,211 @@
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Astar is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Astar. If not, see .
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub mod weights;
+
+use frame_support::traits::nonfungibles::{Inspect, InspectEnumerable};
+use pallet_contracts::chain_extension::{
+ ChainExtension, Environment, Ext, InitState, RetVal, SysConfig,
+};
+use parity_scale_codec::Encode;
+use sp_runtime::traits::StaticLookup;
+use sp_runtime::BoundedVec;
+use sp_runtime::DispatchError;
+use sp_std::marker::PhantomData;
+use sp_std::vec::Vec;
+use uniques_chain_extension_types::Outcome;
+
+type AccountIdLookup = <::Lookup as StaticLookup>::Source;
+
+enum UniquesFunc {
+ Owner,
+ CollectionOwner,
+ Attribute,
+ CollectionAttribute,
+ CanTransfer,
+ Collections,
+ Items,
+ Owned,
+ OwnedInCollection,
+}
+
+impl TryFrom for UniquesFunc {
+ type Error = DispatchError;
+
+ fn try_from(value: u16) -> Result {
+ match value {
+ 1 => Ok(UniquesFunc::Owner),
+ 2 => Ok(UniquesFunc::CollectionOwner),
+ 3 => Ok(UniquesFunc::Attribute),
+ 4 => Ok(UniquesFunc::CollectionAttribute),
+ 5 => Ok(UniquesFunc::CanTransfer),
+ 6 => Ok(UniquesFunc::Collections),
+ 7 => Ok(UniquesFunc::Items),
+ 8 => Ok(UniquesFunc::Owned),
+ 9 => Ok(UniquesFunc::OwnedInCollection),
+ _ => Err(DispatchError::Other(
+ "Unimplemented func_id for UniquesFunc",
+ )),
+ }
+ }
+}
+
+/// Pallet Uniques chain extension.
+pub struct UniquesExtension(PhantomData<(T, W)>);
+
+impl Default for UniquesExtension {
+ fn default() -> Self {
+ UniquesExtension(PhantomData)
+ }
+}
+
+impl ChainExtension for UniquesExtension
+where
+ T: pallet_uniques::Config + pallet_contracts::Config,
+ AccountIdLookup: From<::AccountId>,
+ ::AccountId: From<[u8; 32]>,
+ W: weights::WeightInfo,
+{
+ fn call(&mut self, env: Environment) -> Result
+ where
+ E: Ext,
+ {
+ let func_id = env.func_id().try_into()?;
+ let mut env = env.buf_in_buf_out();
+
+ match func_id {
+ UniquesFunc::Owner => {
+ let (collection_id, item): (
+ ::CollectionId,
+ ::ItemId,
+ ) = env.read_as()?;
+
+ let base_weight = ::owner();
+ env.charge_weight(base_weight)?;
+
+ let owner = pallet_uniques::Pallet::::owner(collection_id, item);
+ env.write(&owner.encode(), false, None)?;
+ }
+ UniquesFunc::CollectionOwner => {
+ let collection_id: ::CollectionId = env.read_as()?;
+
+ let base_weight = ::collection_owner();
+ env.charge_weight(base_weight)?;
+
+ let owner = pallet_uniques::Pallet::::collection_owner(collection_id);
+ env.write(&owner.encode(), false, None)?;
+ }
+ UniquesFunc::Attribute => {
+ let (collection_id, item, key): (
+ ::CollectionId,
+ ::ItemId,
+ BoundedVec::KeyLimit>,
+ ) = env.read_as()?;
+
+ let base_weight = ::attribute();
+ env.charge_weight(base_weight)?;
+
+ let attribute = pallet_uniques::Pallet::::attribute(&collection_id, &item, &key);
+ env.write(&attribute.encode(), false, None)?;
+ }
+ UniquesFunc::CollectionAttribute => {
+ let (collection_id, key): (
+ ::CollectionId,
+ BoundedVec::KeyLimit>,
+ ) = env.read_as()?;
+
+ let base_weight = ::collection_attribute();
+ env.charge_weight(base_weight)?;
+
+ let attribute =
+ pallet_uniques::Pallet::::collection_attribute(&collection_id, &key);
+ env.write(&attribute.encode(), false, None)?;
+ }
+ UniquesFunc::CanTransfer => {
+ let (collection_id, item): (
+ ::CollectionId,
+ ::ItemId,
+ ) = env.read_as()?;
+
+ let base_weight = ::can_transfer();
+ env.charge_weight(base_weight)?;
+
+ let can_transfer = pallet_uniques::Pallet::::can_transfer(&collection_id, &item);
+ env.write(&can_transfer.encode(), false, None)?;
+ }
+ UniquesFunc::Collections => {
+ let read_bound: u32 = env.read_as()?;
+
+ let base_weight = ::collections(read_bound);
+ env.charge_weight(base_weight)?;
+
+ let collections: Vec<::CollectionId> =
+ pallet_uniques::Pallet::::collections().collect();
+
+ env.write(&collections.encode(), false, None)?;
+ }
+ UniquesFunc::Items => {
+ let (collection_id, read_bound): (
+ ::CollectionId,
+ u32,
+ ) = env.read_as()?;
+
+ let base_weight = ::items(read_bound);
+ env.charge_weight(base_weight)?;
+
+ let items: Vec<::ItemId> =
+ pallet_uniques::Pallet::::items(&collection_id).collect();
+
+ env.write(&items.encode(), false, None)?;
+ }
+ UniquesFunc::Owned => {
+ let (who, read_bound): (T::AccountId, u32) = env.read_as()?;
+
+ let items: Vec<(
+ ::CollectionId,
+ ::ItemId,
+ )> = pallet_uniques::Pallet::::owned(&who).collect();
+
+ let base_weight = ::owned(read_bound);
+ env.charge_weight(base_weight)?;
+
+ env.write(&items.encode(), false, None)?;
+ }
+ UniquesFunc::OwnedInCollection => {
+ let (who, collection_id, read_bound): (
+ T::AccountId,
+ ::CollectionId,
+ u32,
+ ) = env.read_as()?;
+
+ let items: Vec<::ItemId> =
+ pallet_uniques::Pallet::::owned_in_collection(&collection_id, &who)
+ .collect();
+
+ let base_weight = ::owned_in_collection(read_bound);
+ env.charge_weight(base_weight)?;
+
+ env.write(&items.encode(), false, None)?;
+ }
+ }
+
+ Ok(RetVal::Converging(Outcome::Success as u32))
+ }
+}
diff --git a/chain-extensions/pallet-uniques/src/weights.rs b/chain-extensions/pallet-uniques/src/weights.rs
new file mode 100644
index 0000000000..fb847b3abb
--- /dev/null
+++ b/chain-extensions/pallet-uniques/src/weights.rs
@@ -0,0 +1,77 @@
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Astar is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Astar. If not, see .
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet-uniques chain-extension.
+pub trait WeightInfo {
+ fn owner() -> Weight;
+ fn collection_owner() -> Weight;
+ fn attribute() -> Weight;
+ fn collection_attribute() -> Weight;
+ fn can_transfer() -> Weight;
+ fn collections(n: u32) -> Weight;
+ fn items(n: u32) -> Weight;
+ fn owned(n: u32) -> Weight;
+ fn owned_in_collection(n: u32) -> Weight;
+}
+
+/// Weights for pallet-uniques chain-extension
+pub struct SubstrateWeight(PhantomData);
+impl WeightInfo for SubstrateWeight {
+ fn owner() -> Weight {
+ T::DbWeight::get().reads(1 as u64)
+ }
+
+ fn collection_owner() -> Weight {
+ T::DbWeight::get().reads(1 as u64)
+ }
+
+ fn attribute() -> Weight {
+ T::DbWeight::get().reads(1 as u64)
+ }
+
+ fn collection_attribute() -> Weight {
+ T::DbWeight::get().reads(1 as u64)
+ }
+
+ fn can_transfer() -> Weight {
+ T::DbWeight::get().reads(2 as u64)
+ }
+
+ fn collections(n: u32) -> Weight {
+ T::DbWeight::get().reads(1 as u64).saturating_mul(n.into())
+ }
+
+ fn items(n: u32) -> Weight {
+ T::DbWeight::get().reads(1 as u64).saturating_mul(n.into())
+ }
+
+ fn owned(n: u32) -> Weight {
+ T::DbWeight::get().reads(1 as u64).saturating_mul(n.into())
+ }
+
+ fn owned_in_collection(n: u32) -> Weight {
+ T::DbWeight::get().reads(1 as u64).saturating_mul(n.into())
+ }
+}
diff --git a/chain-extensions/types/uniques/Cargo.toml b/chain-extensions/types/uniques/Cargo.toml
new file mode 100644
index 0000000000..d5c386bcd5
--- /dev/null
+++ b/chain-extensions/types/uniques/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "uniques-chain-extension-types"
+version = "0.1.0"
+license = "Apache-2.0"
+description = "Types definitions for uniques chain-extension"
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+parity-scale-codec = { workspace = true }
+scale-info = { workspace = true }
+
+frame-system = { workspace = true }
+pallet-contracts = { workspace = true }
+
+[features]
+default = ["std"]
+std = [
+ "scale-info/std",
+ "parity-scale-codec/std",
+ "pallet-contracts/std",
+ "frame-system/std",
+]
diff --git a/chain-extensions/types/uniques/src/lib.rs b/chain-extensions/types/uniques/src/lib.rs
new file mode 100644
index 0000000000..675562901f
--- /dev/null
+++ b/chain-extensions/types/uniques/src/lib.rs
@@ -0,0 +1,37 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+use parity_scale_codec::MaxEncodedLen;
+use parity_scale_codec::{Decode, Encode};
+
+#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug)]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
+pub enum Outcome {
+ /// Success
+ Success = 0,
+ /// Origin Caller is not supported
+ OriginCannotBeCaller = 98,
+ /// Unknown error
+ RuntimeError = 99,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen)]
+#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
+pub enum Origin {
+ Caller,
+ Address,
+}
+
+impl Default for Origin {
+ fn default() -> Self {
+ Self::Address
+ }
+}
+
+#[macro_export]
+macro_rules! select_origin {
+ ($origin:expr, $account:expr) => {
+ match $origin {
+ Origin::Caller => return Ok(RetVal::Converging(Outcome::OriginCannotBeCaller as u32)),
+ Origin::Address => RawOrigin::Signed($account),
+ }
+ };
+}
diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml
index d2f846ae7f..431875ce82 100644
--- a/runtime/local/Cargo.toml
+++ b/runtime/local/Cargo.toml
@@ -96,6 +96,7 @@ moonbeam-rpc-primitives-txpool = { workspace = true, optional = true }
# chain-extensions
pallet-chain-extension-assets = { workspace = true }
+pallet-chain-extension-uniques = { workspace = true }
# benchmarking
array-bytes = { workspace = true }
@@ -184,6 +185,7 @@ std = [
"moonbeam-rpc-primitives-txpool/std",
"substrate-wasm-builder",
"pallet-chain-extension-assets/std",
+ "pallet-chain-extension-uniques/std",
"astar-primitives/std",
]
runtime-benchmarks = [
diff --git a/runtime/local/src/chain_extensions.rs b/runtime/local/src/chain_extensions.rs
index a368fa8d18..b1cb947773 100644
--- a/runtime/local/src/chain_extensions.rs
+++ b/runtime/local/src/chain_extensions.rs
@@ -20,6 +20,7 @@ use super::{Runtime, UnifiedAccounts, Xvm};
/// Registered WASM contracts chain extensions.
pub use pallet_chain_extension_assets::AssetsExtension;
+pub use pallet_chain_extension_uniques::UniquesExtension;
use pallet_contracts::chain_extension::RegisteredChainExtension;
pub use pallet_chain_extension_unified_accounts::UnifiedAccountsExtension;
@@ -40,3 +41,9 @@ impl RegisteredChainExten
impl RegisteredChainExtension for UnifiedAccountsExtension {
const ID: u16 = 03;
}
+
+impl RegisteredChainExtension
+ for UniquesExtension
+{
+ const ID: u16 = 04;
+}
diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs
index 611cb78485..e5ef9e19a7 100644
--- a/runtime/local/src/lib.rs
+++ b/runtime/local/src/lib.rs
@@ -930,6 +930,7 @@ impl pallet_contracts::Config for Runtime {
XvmExtension,
AssetsExtension>,
UnifiedAccountsExtension,
+ UniquesExtension>,
);
type Schedule = Schedule;
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;
diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml
index 4519a2db5f..8393491ee2 100644
--- a/runtime/shibuya/Cargo.toml
+++ b/runtime/shibuya/Cargo.toml
@@ -130,6 +130,7 @@ moonbeam-rpc-primitives-txpool = { workspace = true, optional = true }
# chain-extensions
pallet-chain-extension-assets = { workspace = true }
+pallet-chain-extension-uniques = { workspace = true }
# benchmarking
array-bytes = { workspace = true }
@@ -242,6 +243,7 @@ std = [
"xcm-executor/std",
"substrate-wasm-builder",
"pallet-chain-extension-assets/std",
+ "pallet-chain-extension-uniques/std",
"orml-xtokens/std",
"orml-xcm-support/std",
"astar-primitives/std",
diff --git a/runtime/shibuya/src/chain_extensions.rs b/runtime/shibuya/src/chain_extensions.rs
index a368fa8d18..b1cb947773 100644
--- a/runtime/shibuya/src/chain_extensions.rs
+++ b/runtime/shibuya/src/chain_extensions.rs
@@ -20,6 +20,7 @@ use super::{Runtime, UnifiedAccounts, Xvm};
/// Registered WASM contracts chain extensions.
pub use pallet_chain_extension_assets::AssetsExtension;
+pub use pallet_chain_extension_uniques::UniquesExtension;
use pallet_contracts::chain_extension::RegisteredChainExtension;
pub use pallet_chain_extension_unified_accounts::UnifiedAccountsExtension;
@@ -40,3 +41,9 @@ impl RegisteredChainExten
impl RegisteredChainExtension for UnifiedAccountsExtension {
const ID: u16 = 03;
}
+
+impl RegisteredChainExtension
+ for UniquesExtension
+{
+ const ID: u16 = 04;
+}
diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs
index d9f199a9d3..7c4b74d4aa 100644
--- a/runtime/shibuya/src/lib.rs
+++ b/runtime/shibuya/src/lib.rs
@@ -719,6 +719,7 @@ impl pallet_contracts::Config for Runtime {
XvmExtension,
AssetsExtension>,
UnifiedAccountsExtension,
+ UniquesExtension>,
);
type Schedule = Schedule;
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;