difftreelog
Merge branch 'develop' into tests/generalization
in: master
57 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2330,7 +2330,6 @@
"hex",
"hex-literal",
"impl-trait-for-tuples",
- "pallet-evm",
"primitive-types 0.12.1",
"sha3-const",
"similar-asserts",
crates/evm-coder/Cargo.tomldiffbeforeafterboth--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -19,8 +19,6 @@
# We have tuple-heavy code in solidity.rs
impl-trait-for-tuples = "0.2.2"
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.36" }
-
[dev-dependencies]
# We want to assert some large binary blobs equality in tests
hex = "0.4.3"
crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -347,14 +347,14 @@
fn is_value(&self) -> bool {
if let Ok(ident) = self.plain() {
- return ident == "value";
+ return ident == "Value";
}
false
}
fn is_caller(&self) -> bool {
if let Ok(ident) = self.plain() {
- return ident == "caller";
+ return ident == "Caller";
}
false
}
@@ -610,7 +610,7 @@
let custom_signature = self.expand_custom_signature();
quote! {
const #screaming_name_signature: ::evm_coder::custom_signature::SignatureUnit = #custom_signature;
- const #screaming_name: ::evm_coder::types::bytes4 = {
+ const #screaming_name: ::evm_coder::types::Bytes4 = {
let mut sum = ::evm_coder::sha3_const::Keccak256::new();
let mut pos = 0;
while pos < Self::#screaming_name_signature.len {
@@ -974,7 +974,7 @@
#consts
)*
/// Return this call ERC165 selector
- pub const fn interface_id() -> ::evm_coder::types::bytes4 {
+ pub const fn interface_id() -> ::evm_coder::types::Bytes4 {
let mut interface_id = 0;
#(#interface_id)*
#(#inline_interface_id)*
@@ -999,7 +999,7 @@
)*),
};
- let mut out = ::evm_coder::types::string::new();
+ let mut out = ::evm_coder::types::String::new();
if #solidity_name.starts_with("Inline") {
out.push_str("/// @dev inlined interface\n");
}
@@ -1019,7 +1019,7 @@
}
}
impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
- fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
+ fn parse(method_id: ::evm_coder::types::Bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
use ::evm_coder::abi::AbiRead;
match method_id {
::evm_coder::ERC165Call::INTERFACE_ID => return Ok(
@@ -1041,7 +1041,7 @@
#gen_where
{
/// Is this contract implements specified ERC165 selector
- pub fn supports_interface(this: &#name, interface_id: ::evm_coder::types::bytes4) -> bool {
+ pub fn supports_interface(this: &#name, interface_id: ::evm_coder::types::Bytes4) -> bool {
interface_id != u32::to_be_bytes(0xffffff) && (
interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||
interface_id == Self::interface_id()
crates/evm-coder/procedural/src/to_log.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/to_log.rs
+++ b/crates/evm-coder/procedural/src/to_log.rs
@@ -137,7 +137,7 @@
Self::#name {#(
#fields,
)*} => {
- topics.push(topic::from(Self::#name_screaming));
+ topics.push(::evm_coder::types::Topic::from(Self::#name_screaming));
#(
topics.push(#indexed.to_topic());
)*
@@ -222,7 +222,7 @@
#solidity_functions,
)*),
};
- let mut out = string::new();
+ let mut out = ::evm_coder::types::String::new();
out.push_str("/// @dev inlined interface\n");
let _ = interface.format(is_impl, &mut out, tc);
tc.collect(out);
@@ -231,7 +231,7 @@
#[automatically_derived]
impl ::evm_coder::events::ToLog for #name {
- fn to_log(&self, contract: address) -> ::ethereum::Log {
+ fn to_log(&self, contract: Address) -> ::ethereum::Log {
use ::evm_coder::events::ToTopic;
use ::evm_coder::abi::AbiWrite;
let mut writer = ::evm_coder::abi::AbiWriter::new();
crates/evm-coder/src/abi/impls.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/impls.rs
+++ b/crates/evm-coder/src/abi/impls.rs
@@ -63,27 +63,27 @@
impl_abi!(u128, uint128, false);
impl_abi!(U256, uint256, false);
impl_abi!(H160, address, false);
-impl_abi!(string, string, true);
+impl_abi!(String, string, true);
impl_abi_writeable!(&str, string);
-impl_abi_type!(bytes, bytes, true);
+impl_abi_type!(Bytes, bytes, true);
-impl AbiRead for bytes {
- fn abi_read(reader: &mut AbiReader) -> Result<bytes> {
- Ok(bytes(reader.bytes()?))
+impl AbiRead for Bytes {
+ fn abi_read(reader: &mut AbiReader) -> Result<Bytes> {
+ Ok(Bytes(reader.bytes()?))
}
}
-impl AbiWrite for bytes {
+impl AbiWrite for Bytes {
fn abi_write(&self, writer: &mut AbiWriter) {
writer.bytes(self.0.as_slice())
}
}
-impl_abi_type!(bytes4, bytes4, false);
-impl AbiRead for bytes4 {
- fn abi_read(reader: &mut AbiReader) -> Result<bytes4> {
+impl_abi_type!(Bytes4, bytes4, false);
+impl AbiRead for Bytes4 {
+ fn abi_read(reader: &mut AbiReader) -> Result<Bytes4> {
reader.bytes4()
}
}
crates/evm-coder/src/abi/mod.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/mod.rs
+++ b/crates/evm-coder/src/abi/mod.rs
@@ -54,7 +54,7 @@
}
}
/// Start reading RLP buffer, parsing first 4 bytes as selector
- pub fn new_call(buf: &'i [u8]) -> Result<(bytes4, Self)> {
+ pub fn new_call(buf: &'i [u8]) -> Result<(Bytes4, Self)> {
if buf.len() < 4 {
return Err(Error::Error(ExitError::OutOfOffset));
}
@@ -148,8 +148,8 @@
}
/// Read [`string`] at current position, then advance
- pub fn string(&mut self) -> Result<string> {
- string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
+ pub fn string(&mut self) -> Result<String> {
+ String::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
}
/// Read [`u8`] at current position, then advance
crates/evm-coder/src/abi/test.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/test.rs
+++ b/crates/evm-coder/src/abi/test.rs
@@ -39,22 +39,22 @@
#[test]
fn encode_decode_uint8() {
- test_impl_uint!(uint8);
+ test_impl_uint!(u8);
}
#[test]
fn encode_decode_uint32() {
- test_impl_uint!(uint32);
+ test_impl_uint!(u32);
}
#[test]
fn encode_decode_uint128() {
- test_impl_uint!(uint128);
+ test_impl_uint!(u128);
}
#[test]
fn encode_decode_uint256() {
- test_impl::<uint256>(
+ test_impl::<U256>(
0xdeadbeef,
U256([255, 0, 0, 0]),
&hex!(
@@ -101,7 +101,7 @@
#[test]
fn encode_decode_vec_tuple_address_uint256() {
- test_impl::<Vec<(address, uint256)>>(
+ test_impl::<Vec<(Address, U256)>>(
0x1ACF2D55,
vec![
(
@@ -138,7 +138,7 @@
#[test]
fn encode_decode_vec_tuple_uint256_string() {
- test_impl::<Vec<(uint256, string)>>(
+ test_impl::<Vec<(U256, String)>>(
0xdeadbeef,
vec![
(1.into(), "Test URI 0".to_string()),
@@ -261,7 +261,7 @@
let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
assert_eq!(call, u32::to_be_bytes(decoded_data.0));
let address = decoder.address().unwrap();
- let data = <Vec<(uint256, string)>>::abi_read(&mut decoder).unwrap();
+ let data = <Vec<(U256, String)>>::abi_read(&mut decoder).unwrap();
assert_eq!(data, decoded_data.1);
let mut writer = AbiWriter::new_call(decoded_data.0);
@@ -273,12 +273,12 @@
#[test]
fn encode_decode_vec_tuple_string_bytes() {
- test_impl::<Vec<(string, bytes)>>(
+ test_impl::<Vec<(String, Bytes)>>(
0xdeadbeef,
vec![
(
"Test URI 0".to_string(),
- bytes(vec![
+ Bytes(vec![
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
@@ -287,14 +287,14 @@
),
(
"Test URI 1".to_string(),
- bytes(vec![
+ Bytes(vec![
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
]),
),
- ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])),
+ ("Test URI 2".to_string(), Bytes(vec![0x33, 0x33])),
],
&hex!(
"
@@ -337,10 +337,10 @@
// #[ignore = "reason"]
fn encode_decode_tuple0_tuple1_uint8_tuple1_string_bytes_tuple1_uint8_bytes() {
let int = 0xff;
- let by = bytes(vec![0x11, 0x22, 0x33]);
+ let by = Bytes(vec![0x11, 0x22, 0x33]);
let string = "some string".to_string();
- test_impl::<((u8,), (String, bytes), (u8, bytes))>(
+ test_impl::<((u8,), (String, Bytes), (u8, Bytes))>(
0xdeadbeef,
((int,), (string.clone(), by.clone()), (int, by)),
&hex!(
@@ -485,9 +485,9 @@
#[test]
fn encode_decode_tuple0_tuple1_string_bytes() {
- test_impl::<((String, bytes),)>(
+ test_impl::<((String, Bytes),)>(
0xdeadbeef,
- (("some string".to_string(), bytes(vec![1, 2, 3])),),
+ (("some string".to_string(), Bytes(vec![1, 2, 3])),),
&hex!(
"
deadbeef
crates/evm-coder/src/events.rsdiffbeforeafterboth--- a/crates/evm-coder/src/events.rs
+++ b/crates/evm-coder/src/events.rs
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
use ethereum::Log;
-use primitive_types::{H160, H256};
+use primitive_types::{H160, H256, U256};
use crate::types::*;
@@ -45,7 +45,7 @@
}
}
-impl ToTopic for uint256 {
+impl ToTopic for U256 {
fn to_topic(&self) -> H256 {
let mut out = [0u8; 32];
self.to_big_endian(&mut out);
@@ -53,7 +53,7 @@
}
}
-impl ToTopic for address {
+impl ToTopic for Address {
fn to_topic(&self) -> H256 {
let mut out = [0u8; 32];
out[12..32].copy_from_slice(&self.0);
@@ -61,7 +61,7 @@
}
}
-impl ToTopic for uint32 {
+impl ToTopic for u32 {
fn to_topic(&self) -> H256 {
let mut out = [0u8; 32];
out[28..32].copy_from_slice(&self.to_be_bytes());
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -131,33 +131,23 @@
use alloc::{vec::Vec};
use primitive_types::{U256, H160, H256};
- pub type address = H160;
- pub type uint8 = u8;
- pub type uint16 = u16;
- pub type uint32 = u32;
- pub type uint64 = u64;
- pub type uint128 = u128;
- pub type uint256 = U256;
- pub type bytes4 = [u8; 4];
- pub type topic = H256;
+ pub type Address = H160;
+ pub type Bytes4 = [u8; 4];
+ pub type Topic = H256;
#[cfg(not(feature = "std"))]
- pub type string = ::alloc::string::String;
+ pub type String = ::alloc::string::String;
#[cfg(feature = "std")]
- pub type string = ::std::string::String;
+ pub type String = ::std::string::String;
#[derive(Default, Debug, PartialEq, Eq, Clone)]
- pub struct bytes(pub Vec<u8>);
-
- /// Solidity doesn't have `void` type, however we have special implementation
- /// for empty tuple return type
- pub type void = ();
+ pub struct Bytes(pub Vec<u8>);
//#region Special types
/// Makes function payable
- pub type value = U256;
+ pub type Value = U256;
/// Makes function caller-sensitive
- pub type caller = address;
+ pub type Caller = Address;
//#endregion
/// Ethereum typed call message, similar to solidity
@@ -172,20 +162,20 @@
pub value: U256,
}
- impl From<Vec<u8>> for bytes {
+ impl From<Vec<u8>> for Bytes {
fn from(src: Vec<u8>) -> Self {
Self(src)
}
}
#[allow(clippy::from_over_into)]
- impl Into<Vec<u8>> for bytes {
+ impl Into<Vec<u8>> for Bytes {
fn into(self) -> Vec<u8> {
self.0
}
}
- impl bytes {
+ impl Bytes {
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
@@ -201,7 +191,7 @@
/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro
pub trait Call: Sized {
/// Parse call buffer into typed call enum
- fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;
+ fn parse(selector: types::Bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;
}
/// Intended to be used as `#[weight]` output type
@@ -237,22 +227,22 @@
/// implements specified interface
SupportsInterface {
/// Requested interface
- interface_id: types::bytes4,
+ interface_id: types::Bytes4,
},
}
impl ERC165Call {
/// ERC165 selector is provided by standard
- pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);
+ pub const INTERFACE_ID: types::Bytes4 = u32::to_be_bytes(0x01ffc9a7);
}
impl Call for ERC165Call {
- fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>> {
+ fn parse(selector: types::Bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>> {
if selector != Self::INTERFACE_ID {
return Ok(None);
}
Ok(Some(Self::SupportsInterface {
- interface_id: types::bytes4::abi_read(input)?,
+ interface_id: types::Bytes4::abi_read(input)?,
}))
}
}
crates/evm-coder/src/solidity/impls.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity/impls.rs
+++ b/crates/evm-coder/src/solidity/impls.rs
@@ -27,14 +27,14 @@
u64 => "uint64" true = "0",
u128 => "uint128" true = "0",
U256 => "uint256" true = "0",
- bytes4 => "bytes4" true = "bytes4(0)",
+ Bytes4 => "bytes4" true = "bytes4(0)",
H160 => "address" true = "0x0000000000000000000000000000000000000000",
- string => "string" false = "\"\"",
- bytes => "bytes" false = "hex\"\"",
+ String => "string" false = "\"\"",
+ Bytes => "bytes" false = "hex\"\"",
bool => "bool" true = "false",
}
-impl SolidityTypeName for void {
+impl SolidityTypeName for () {
fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
Ok(())
}
@@ -72,10 +72,10 @@
macro_rules! impl_tuples {
($($ident:ident)+) => {
impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleTy for ($($ident,)+) {
- fn fields(tc: &TypeCollector) -> Vec<string> {
+ fn fields(tc: &TypeCollector) -> Vec<String> {
let mut collected = Vec::with_capacity(Self::len());
$({
- let mut out = string::new();
+ let mut out = String::new();
$ident::solidity_name(&mut out, tc).expect("no fmt error");
collected.push(out);
})*;
crates/evm-coder/src/solidity/mod.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity/mod.rs
+++ b/crates/evm-coder/src/solidity/mod.rs
@@ -26,7 +26,7 @@
mod impls;
#[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
+use alloc::{vec::Vec, collections::BTreeMap, format};
#[cfg(feature = "std")]
use std::collections::BTreeMap;
use core::{
@@ -42,16 +42,16 @@
pub struct TypeCollector {
/// Code => id
/// id ordering is required to perform topo-sort on the resulting data
- structs: RefCell<BTreeMap<string, usize>>,
- anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
- // generic: RefCell<BTreeMap<string, usize>>,
+ structs: RefCell<BTreeMap<String, usize>>,
+ anonymous: RefCell<BTreeMap<Vec<String>, usize>>,
+ // generic: RefCell<BTreeMap<String, usize>>,
id: Cell<usize>,
}
impl TypeCollector {
pub fn new() -> Self {
Self::default()
}
- pub fn collect(&self, item: string) {
+ pub fn collect(&self, item: String) {
let id = self.next_id();
self.structs.borrow_mut().insert(item, id);
}
@@ -84,7 +84,7 @@
pub fn collect_enum<T: SolidityEnumTy>(&self) -> String {
T::generate_solidity_interface(self)
}
- pub fn finish(self) -> Vec<string> {
+ pub fn finish(self) -> Vec<String> {
let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();
data.sort_by_key(|(_, id)| Reverse(*id));
data.into_iter().map(|(code, _)| code).collect()
@@ -360,7 +360,7 @@
pub struct SolidityInterface<F: SolidityFunctions> {
pub docs: &'static [&'static str],
- pub selector: bytes4,
+ pub selector: Bytes4,
pub name: &'static str,
pub is: &'static [&'static str],
pub functions: F,
crates/evm-coder/tests/abi_derive_generation.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/abi_derive_generation.rs
+++ b/crates/evm-coder/tests/abi_derive_generation.rs
@@ -1,6 +1,6 @@
mod test_struct {
use evm_coder_procedural::AbiCoder;
- use evm_coder::types::bytes;
+ use evm_coder::types::Bytes;
#[test]
fn empty_struct() {
@@ -27,13 +27,13 @@
#[derive(AbiCoder, PartialEq, Debug)]
struct TypeStruct2DynamicParam {
_a: String,
- _b: bytes,
+ _b: Bytes,
}
#[derive(AbiCoder, PartialEq, Debug)]
struct TypeStruct2MixedParam {
_a: u8,
- _b: bytes,
+ _b: Bytes,
}
#[derive(AbiCoder, PartialEq, Debug)]
@@ -236,10 +236,10 @@
struct TupleStruct2SimpleParam(u8, u32);
#[derive(AbiCoder, PartialEq, Debug)]
- struct TupleStruct2DynamicParam(String, bytes);
+ struct TupleStruct2DynamicParam(String, Bytes);
#[derive(AbiCoder, PartialEq, Debug)]
- struct TupleStruct2MixedParam(u8, bytes);
+ struct TupleStruct2MixedParam(u8, Bytes);
#[derive(AbiCoder, PartialEq, Debug)]
struct TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam);
@@ -562,8 +562,8 @@
#[test]
fn codec_struct_2_dynamic() {
let _a: String = "some string".into();
- let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);
- test_impl::<(String, bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>(
+ let _b: Bytes = Bytes(vec![0x11, 0x22, 0x33]);
+ test_impl::<(String, Bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>(
(_a.clone(), _b.clone()),
TupleStruct2DynamicParam(_a.clone(), _b.clone()),
TypeStruct2DynamicParam { _a, _b },
@@ -573,8 +573,8 @@
#[test]
fn codec_struct_2_mixed() {
let _a: u8 = 0xff;
- let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);
- test_impl::<(u8, bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>(
+ let _b: Bytes = Bytes(vec![0x11, 0x22, 0x33]);
+ test_impl::<(u8, Bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>(
(_a.clone(), _b.clone()),
TupleStruct2MixedParam(_a.clone(), _b.clone()),
TypeStruct2MixedParam { _a, _b },
@@ -605,9 +605,9 @@
#[test]
fn codec_struct_2_derived_dynamic() {
let _a = "some string".to_string();
- let _b = bytes(vec![0x11, 0x22, 0x33]);
+ let _b = Bytes(vec![0x11, 0x22, 0x33]);
test_impl::<
- ((String,), (String, bytes)),
+ ((String,), (String, Bytes)),
TupleStruct2DerivedDynamicParam,
TypeStruct2DerivedDynamicParam,
>(
@@ -626,10 +626,10 @@
#[test]
fn codec_struct_3_derived_mixed() {
let int = 0xff;
- let by = bytes(vec![0x11, 0x22, 0x33]);
+ let by = Bytes(vec![0x11, 0x22, 0x33]);
let string = "some string".to_string();
test_impl::<
- ((u8,), (String, bytes), (u8, bytes)),
+ ((u8,), (String, Bytes), (u8, Bytes)),
TupleStruct3DerivedMixedParam,
TypeStruct3DerivedMixedParam,
>(
crates/evm-coder/tests/conditional_is.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/conditional_is.rs
+++ b/crates/evm-coder/tests/conditional_is.rs
@@ -4,14 +4,14 @@
#[solidity_interface(name = A)]
impl Contract {
- fn method_a() -> Result<void> {
+ fn method_a() -> Result<()> {
Ok(())
}
}
#[solidity_interface(name = B)]
impl Contract {
- fn method_b() -> Result<void> {
+ fn method_b() -> Result<()> {
Ok(())
}
}
crates/evm-coder/tests/generics.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/generics.rs
+++ b/crates/evm-coder/tests/generics.rs
@@ -16,19 +16,20 @@
use std::marker::PhantomData;
use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*};
+use primitive_types::U256;
pub struct Generic<T>(PhantomData<T>);
#[solidity_interface(name = GenericIs)]
impl<T> Generic<T> {
- fn test_1(&self) -> Result<uint256> {
+ fn test_1(&self) -> Result<U256> {
unreachable!()
}
}
#[solidity_interface(name = Generic, is(GenericIs))]
impl<T: Into<u32>> Generic<T> {
- fn test_2(&self) -> Result<uint256> {
+ fn test_2(&self) -> Result<U256> {
unreachable!()
}
}
@@ -40,7 +41,7 @@
where
T: core::fmt::Debug,
{
- fn test_3(&self) -> Result<uint256> {
+ fn test_3(&self) -> Result<U256> {
unreachable!()
}
}
crates/evm-coder/tests/log.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/log.rs
+++ b/crates/evm-coder/tests/log.rs
@@ -17,19 +17,20 @@
#![allow(dead_code)]
use evm_coder::{ToLog, types::*};
+use primitive_types::U256;
#[derive(ToLog)]
enum ERC721Log {
Transfer {
#[indexed]
- from: address,
+ from: Address,
#[indexed]
- to: address,
- value: uint256,
+ to: Address,
+ value: U256,
},
Eee {
#[indexed]
- aaa: address,
- bbb: uint256,
+ aaa: Address,
+ bbb: U256,
},
}
crates/evm-coder/tests/random.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/random.rs
+++ b/crates/evm-coder/tests/random.rs
@@ -19,19 +19,20 @@
use evm_coder::{
abi::AbiType, ToLog, execution::Result, solidity_interface, types::*, solidity, weight,
};
+use primitive_types::U256;
pub struct Impls;
#[solidity_interface(name = OurInterface)]
impl Impls {
- fn fn_a(&self, _input: uint256) -> Result<bool> {
+ fn fn_a(&self, _input: U256) -> Result<bool> {
unreachable!()
}
}
#[solidity_interface(name = OurInterface1)]
impl Impls {
- fn fn_b(&self, _input: uint128) -> Result<uint32> {
+ fn fn_b(&self, _input: u128) -> Result<u32> {
unreachable!()
}
}
@@ -39,12 +40,12 @@
#[derive(ToLog)]
enum OurEvents {
Event1 {
- field1: uint32,
+ field1: u32,
},
Event2 {
- field1: uint32,
+ field1: u32,
#[indexed]
- field2: uint32,
+ field2: u32,
},
}
@@ -56,27 +57,27 @@
)]
impl Impls {
#[solidity(rename_selector = "fnK")]
- fn fn_c(&self, _input: uint32) -> Result<uint8> {
+ fn fn_c(&self, _input: u32) -> Result<u8> {
unreachable!()
}
- fn fn_d(&self, _value: uint32) -> Result<uint32> {
+ fn fn_d(&self, _value: u32) -> Result<u32> {
unreachable!()
}
- fn caller_sensitive(&self, _caller: caller) -> Result<uint8> {
+ fn caller_sensitive(&self, _caller: Caller) -> Result<u8> {
unreachable!()
}
- fn payable(&mut self, _value: value) -> Result<uint8> {
+ fn payable(&mut self, _value: Value) -> Result<u8> {
unreachable!()
}
#[weight(*_weight)]
- fn with_weight(&self, _weight: uint64) -> Result<void> {
+ fn with_weight(&self, _weight: u64) -> Result<()> {
unreachable!()
}
/// Doccoment example
- fn with_doc(&self) -> Result<void> {
+ fn with_doc(&self) -> Result<()> {
unreachable!()
}
}
crates/evm-coder/tests/solidity_generation.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/solidity_generation.rs
+++ b/crates/evm-coder/tests/solidity_generation.rs
@@ -15,34 +15,35 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
use evm_coder::{abi::AbiType, execution::Result, generate_stubgen, solidity_interface, types::*};
+use primitive_types::U256;
pub struct ERC20;
#[solidity_interface(name = ERC20)]
impl ERC20 {
- fn decimals(&self) -> Result<uint8> {
+ fn decimals(&self) -> Result<u8> {
unreachable!()
}
/// Get balance of specified owner
- fn balance_of(&self, _owner: address) -> Result<uint256> {
+ fn balance_of(&self, _owner: Address) -> Result<U256> {
unreachable!()
}
- fn transfer(&mut self, _caller: caller, _to: address, _value: uint256) -> Result<bool> {
+ fn transfer(&mut self, _caller: Caller, _to: Address, _value: U256) -> Result<bool> {
unreachable!()
}
fn transfer_from(
&mut self,
- _caller: caller,
- _from: address,
- _to: address,
- _value: uint256,
+ _caller: Caller,
+ _from: Address,
+ _to: Address,
+ _value: U256,
) -> Result<bool> {
unreachable!()
}
- fn approve(&mut self, _caller: caller, _spender: address, _value: uint256) -> Result<bool> {
+ fn approve(&mut self, _caller: Caller, _spender: Address, _value: U256) -> Result<bool> {
unreachable!()
}
- fn allowance(&self, _owner: address, _spender: address) -> Result<uint256> {
+ fn allowance(&self, _owner: Address, _spender: Address) -> Result<U256> {
unreachable!()
}
}
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -26,6 +26,7 @@
};
use pallet_evm_coder_substrate::dispatch_to_evm;
use sp_std::{vec, vec::Vec};
+use sp_core::U256;
use up_data_structs::{
AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,
SponsoringRateLimit, SponsorshipState,
@@ -42,32 +43,32 @@
CollectionCreated {
/// Collection owner.
#[indexed]
- owner: address,
+ owner: Address,
/// Collection ID.
#[indexed]
- collection_id: address,
+ collection_id: Address,
},
/// The collection has been destroyed.
CollectionDestroyed {
/// Collection ID.
#[indexed]
- collection_id: address,
+ collection_id: Address,
},
/// The collection has been changed.
CollectionChanged {
/// Collection ID.
#[indexed]
- collection_id: address,
+ collection_id: Address,
},
/// The token has been changed.
TokenChanged {
/// Collection ID.
#[indexed]
- collection_id: address,
+ collection_id: Address,
/// Token ID.
- token_id: uint256,
+ token_id: U256,
},
}
@@ -93,12 +94,7 @@
/// @param value Propery value.
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::set_collection_properties(1))]
- fn set_collection_property(
- &mut self,
- caller: caller,
- key: string,
- value: bytes,
- ) -> Result<void> {
+ fn set_collection_property(&mut self, caller: Caller, key: String, value: Bytes) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let key = <Vec<u8>>::from(key)
.try_into()
@@ -115,9 +111,9 @@
#[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]
fn set_collection_properties(
&mut self,
- caller: caller,
+ caller: Caller,
properties: Vec<eth::Property>,
- ) -> Result<void> {
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let properties = properties
@@ -134,7 +130,7 @@
/// @param key Property key.
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]
- fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {
+ fn delete_collection_property(&mut self, caller: Caller, key: String) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let key = <Vec<u8>>::from(key)
.try_into()
@@ -147,7 +143,7 @@
///
/// @param keys Properties keys.
#[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]
- fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {
+ fn delete_collection_properties(&mut self, caller: Caller, keys: Vec<String>) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let keys = keys
.into_iter()
@@ -168,7 +164,7 @@
///
/// @param key Property key.
/// @return bytes The property corresponding to the key.
- fn collection_property(&self, key: string) -> Result<bytes> {
+ fn collection_property(&self, key: String) -> Result<Bytes> {
let key = <Vec<u8>>::from(key)
.try_into()
.map_err(|_| "key too large")?;
@@ -176,14 +172,14 @@
let props = CollectionProperties::<T>::get(self.id);
let prop = props.get(&key).ok_or("key not found")?;
- Ok(bytes(prop.to_vec()))
+ Ok(Bytes(prop.to_vec()))
}
/// Get collection properties.
///
/// @param keys Properties keys. Empty keys for all propertyes.
/// @return Vector of properties key/value pairs.
- fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<eth::Property>> {
+ fn collection_properties(&self, keys: Vec<String>) -> Result<Vec<eth::Property>> {
let keys = keys
.into_iter()
.map(|key| {
@@ -212,7 +208,7 @@
///
/// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
#[solidity(hide)]
- fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {
+ fn set_collection_sponsor(&mut self, caller: Caller, sponsor: Address) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -229,9 +225,9 @@
/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
fn set_collection_sponsor_cross(
&mut self,
- caller: caller,
+ caller: Caller,
sponsor: eth::CrossAddress,
- ) -> Result<void> {
+ ) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -252,7 +248,7 @@
/// Collection sponsorship confirmation.
///
/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
- fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {
+ fn confirm_collection_sponsorship(&mut self, caller: Caller) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -261,7 +257,7 @@
}
/// Remove collection sponsor.
- fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {
+ fn remove_collection_sponsor(&mut self, caller: Caller) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
let caller = T::CrossAccountId::from_eth(caller);
self.remove_sponsor(&caller).map_err(dispatch_to_evm::<T>)
@@ -343,11 +339,7 @@
/// @dev Throws error if limit not found.
/// @param limit Some limit.
#[solidity(rename_selector = "setCollectionLimit")]
- fn set_collection_limit(
- &mut self,
- caller: caller,
- limit: eth::CollectionLimit,
- ) -> Result<void> {
+ fn set_collection_limit(&mut self, caller: Caller, limit: eth::CollectionLimit) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
if !limit.has_value() {
@@ -359,7 +351,7 @@
}
/// Get contract address.
- fn contract_address(&self) -> Result<address> {
+ fn contract_address(&self) -> Result<Address> {
Ok(crate::eth::collection_id_to_address(self.id))
}
@@ -367,9 +359,9 @@
/// @param newAdmin Cross account administrator address.
fn add_collection_admin_cross(
&mut self,
- caller: caller,
+ caller: Caller,
new_admin: eth::CrossAddress,
- ) -> Result<void> {
+ ) -> Result<()> {
self.consume_store_reads_and_writes(2, 2)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -382,9 +374,9 @@
/// @param admin Cross account administrator address.
fn remove_collection_admin_cross(
&mut self,
- caller: caller,
+ caller: Caller,
admin: eth::CrossAddress,
- ) -> Result<void> {
+ ) -> Result<()> {
self.consume_store_reads_and_writes(2, 2)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -396,7 +388,7 @@
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
#[solidity(hide)]
- fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {
+ fn add_collection_admin(&mut self, caller: Caller, new_admin: Address) -> Result<()> {
self.consume_store_reads_and_writes(2, 2)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -409,7 +401,7 @@
///
/// @param admin Address of the removed administrator.
#[solidity(hide)]
- fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {
+ fn remove_collection_admin(&mut self, caller: Caller, admin: Address) -> Result<()> {
self.consume_store_reads_and_writes(2, 2)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -422,7 +414,7 @@
///
/// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
#[solidity(rename_selector = "setCollectionNesting")]
- fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {
+ fn set_nesting_bool(&mut self, caller: Caller, enable: bool) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -443,10 +435,10 @@
#[solidity(rename_selector = "setCollectionNesting")]
fn set_nesting(
&mut self,
- caller: caller,
+ caller: Caller,
enable: bool,
- collections: Vec<address>,
- ) -> Result<void> {
+ collections: Vec<Address>,
+ ) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
if collections.is_empty() {
@@ -511,18 +503,12 @@
}
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
- fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {
+ fn set_collection_access(&mut self, caller: Caller, mode: eth::AccessMode) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
let caller = T::CrossAccountId::from_eth(caller);
let permissions = CollectionPermissions {
- access: Some(match mode {
- 0 => AccessMode::Normal,
- 1 => AccessMode::AllowList,
- _ => return Err("not supported access mode".into()),
- }),
+ access: Some(mode.into()),
..Default::default()
};
<Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)
@@ -540,7 +526,7 @@
///
/// @param user Address of a trusted user.
#[solidity(hide)]
- fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {
+ fn add_to_collection_allow_list(&mut self, caller: Caller, user: Address) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -554,9 +540,9 @@
/// @param user User cross account address.
fn add_to_collection_allow_list_cross(
&mut self,
- caller: caller,
+ caller: Caller,
user: eth::CrossAddress,
- ) -> Result<void> {
+ ) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -569,7 +555,7 @@
///
/// @param user Address of a removed user.
#[solidity(hide)]
- fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {
+ fn remove_from_collection_allow_list(&mut self, caller: Caller, user: Address) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -583,9 +569,9 @@
/// @param user User cross account address.
fn remove_from_collection_allow_list_cross(
&mut self,
- caller: caller,
+ caller: Caller,
user: eth::CrossAddress,
- ) -> Result<void> {
+ ) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -597,7 +583,7 @@
/// Switch permission for minting.
///
/// @param mode Enable if "true".
- fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {
+ fn set_collection_mint_mode(&mut self, caller: Caller, mode: bool) -> Result<()> {
self.consume_store_reads_and_writes(1, 1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -613,7 +599,7 @@
/// @param user account to verify
/// @return "true" if account is the owner or admin
#[solidity(hide, rename_selector = "isOwnerOrAdmin")]
- fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {
+ fn is_owner_or_admin_eth(&self, user: Address) -> Result<bool> {
let user = T::CrossAccountId::from_eth(user);
Ok(self.is_owner_or_admin(&user))
}
@@ -630,7 +616,7 @@
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
- fn unique_collection_type(&self) -> Result<string> {
+ fn unique_collection_type(&self) -> Result<String> {
let mode = match self.collection.mode {
CollectionMode::Fungible(_) => "Fungible",
CollectionMode::NFT => "NFT",
@@ -654,7 +640,7 @@
/// @dev Owner can be changed only by current owner
/// @param newOwner new owner account
#[solidity(hide, rename_selector = "changeCollectionOwner")]
- fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {
+ fn set_owner(&mut self, caller: Caller, new_owner: Address) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
@@ -680,9 +666,9 @@
/// @param newOwner new owner cross account
fn change_collection_owner_cross(
&mut self,
- caller: caller,
+ caller: Caller,
new_owner: eth::CrossAddress,
- ) -> Result<void> {
+ ) -> Result<()> {
self.consume_store_writes(1)?;
let caller = T::CrossAccountId::from_eth(caller);
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -18,12 +18,9 @@
use alloc::format;
use sp_std::{vec, vec::Vec};
-use evm_coder::{
- AbiCoder,
- types::{uint256, address},
-};
+use evm_coder::{AbiCoder, types::Address};
pub use pallet_evm::{Config, account::CrossAccountId};
-use sp_core::H160;
+use sp_core::{H160, U256};
use up_data_structs::CollectionId;
// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 1
@@ -33,7 +30,7 @@
];
/// Maps the ethereum address of the collection in substrate.
-pub fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {
+pub fn map_eth_to_id(eth: &Address) -> Option<CollectionId> {
if eth[0..16] != ETH_COLLECTION_PREFIX {
return None;
}
@@ -43,7 +40,7 @@
}
/// Maps the substrate collection id in ethereum.
-pub fn collection_id_to_address(id: CollectionId) -> H160 {
+pub fn collection_id_to_address(id: CollectionId) -> Address {
let mut out = [0; 20];
out[0..16].copy_from_slice(Ð_COLLECTION_PREFIX);
out[16..20].copy_from_slice(&u32::to_be_bytes(id.0));
@@ -51,12 +48,12 @@
}
/// Check if the ethereum address is a collection.
-pub fn is_collection(address: &H160) -> bool {
+pub fn is_collection(address: &Address) -> bool {
address[0..16] == ETH_COLLECTION_PREFIX
}
-/// Convert `uint256` to `CrossAccountId`.
-pub fn convert_uint256_to_cross_account<T: Config>(from: uint256) -> T::CrossAccountId
+/// Convert `U256` to `CrossAccountId`.
+pub fn convert_uint256_to_cross_account<T: Config>(from: U256) -> T::CrossAccountId
where
T::AccountId: From<[u8; 32]>,
{
@@ -69,8 +66,8 @@
/// Cross account struct
#[derive(Debug, Default, AbiCoder)]
pub struct CrossAddress {
- pub(crate) eth: address,
- pub(crate) sub: uint256,
+ pub(crate) eth: Address,
+ pub(crate) sub: U256,
}
impl CrossAddress {
@@ -97,7 +94,7 @@
{
Self {
eth: Default::default(),
- sub: uint256::from_big_endian(account_id.as_ref()),
+ sub: U256::from_big_endian(account_id.as_ref()),
}
}
/// Converts [`CrossAddress`] to `CrossAccountId`.
@@ -121,17 +118,17 @@
/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
#[derive(Debug, Default, AbiCoder)]
pub struct Property {
- key: evm_coder::types::string,
- value: evm_coder::types::bytes,
+ key: evm_coder::types::String,
+ value: evm_coder::types::Bytes,
}
impl TryFrom<up_data_structs::Property> for Property {
type Error = evm_coder::execution::Error;
fn try_from(from: up_data_structs::Property) -> Result<Self, Self::Error> {
- let key = evm_coder::types::string::from_utf8(from.key.into())
+ let key = evm_coder::types::String::from_utf8(from.key.into())
.map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {}", e)))?;
- let value = evm_coder::types::bytes(from.value.to_vec());
+ let value = evm_coder::types::Bytes(from.value.to_vec());
Ok(Property { key, value })
}
}
@@ -187,7 +184,7 @@
#[derive(Debug, Default, AbiCoder)]
pub struct CollectionLimit {
field: CollectionLimitField,
- value: Option<uint256>,
+ value: Option<U256>,
}
impl CollectionLimit {
@@ -345,7 +342,7 @@
#[derive(Debug, Default, AbiCoder)]
pub struct TokenPropertyPermission {
/// Token property key.
- key: evm_coder::types::string,
+ key: evm_coder::types::String,
/// Token property permissions.
permissions: Vec<PropertyPermission>,
}
@@ -363,7 +360,7 @@
),
) -> Self {
let (key, permission) = value;
- let key = evm_coder::types::string::from_utf8(key.into_inner())
+ let key = evm_coder::types::String::from_utf8(key.into_inner())
.expect("Stored key must be valid");
let permissions = PropertyPermission::into_vec(permission);
Self { key, permissions }
@@ -393,12 +390,12 @@
#[derive(Debug, Default, AbiCoder)]
pub struct CollectionNesting {
token_owner: bool,
- ids: Vec<uint256>,
+ ids: Vec<U256>,
}
impl CollectionNesting {
/// Create [`CollectionNesting`].
- pub fn new(token_owner: bool, ids: Vec<uint256>) -> Self {
+ pub fn new(token_owner: bool, ids: Vec<U256>) -> Self {
Self { token_owner, ids }
}
}
@@ -416,3 +413,32 @@
Self { field, value }
}
}
+
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+#[derive(AbiCoder, Copy, Clone, Default, Debug)]
+#[repr(u8)]
+pub enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ #[default]
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList,
+}
+
+impl From<up_data_structs::AccessMode> for AccessMode {
+ fn from(value: up_data_structs::AccessMode) -> Self {
+ match value {
+ up_data_structs::AccessMode::Normal => AccessMode::Normal,
+ up_data_structs::AccessMode::AllowList => AccessMode::AllowList,
+ }
+ }
+}
+
+impl Into<up_data_structs::AccessMode> for AccessMode {
+ fn into(self) -> up_data_structs::AccessMode {
+ match self {
+ AccessMode::Normal => up_data_structs::AccessMode::Normal,
+ AccessMode::AllowList => up_data_structs::AccessMode::AllowList,
+ }
+ }
+}
pallets/common/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Common pallet18//!19//! The Common pallet provides functionality for handling collections.20//!21//! ## Overview22//!23//! The Common pallet provides an interface for common collection operations for different collection types24//! (see [CommonCollectionOperations]), as well as a generic dispatcher for these, see [dispatch] module.25//! It also provides this functionality to EVM, see [erc] and [eth] modules.26//!27//! The Common pallet provides functions for:28//!29//! - Setting and approving collection sponsor.30//! - Get\set\delete allow list.31//! - Get\set\delete collection properties.32//! - Get\set\delete collection property permissions.33//! - Get\set\delete token property permissions.34//! - Get\set\delete collection administrators.35//! - Checking access permissions.36//!37//! ### Terminology38//! **Collection sponsor** - For the collection, you can set a sponsor, at whose expense it will39//! be possible to mint tokens.40//!41//! **Allow list** - List of users who have the right to minting tokens.42//!43//! **Collection properties** - Collection properties are simply key-value stores where various44//! metadata can be placed.45//!46//! **Permissions on token properties** - For each property in the token can be set permission47//! to change, see [`PropertyPermission`].48//!49//! **Collection administrator** - For a collection, you can set administrators who have the right50//! to most actions on the collection.5152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63 ensure,64 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65 dispatch::Pays,66 transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70 COLLECTION_NUMBER_LIMIT,71 Collection,72 RpcCollection,73 CollectionFlags,74 RpcCollectionFlags,75 CollectionId,76 CreateItemData,77 MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT,79 TokenId,80 TokenChild,81 CollectionStats,82 MAX_TOKEN_OWNERSHIP,83 CollectionMode,84 NFT_SPONSOR_TRANSFER_TIMEOUT,85 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87 MAX_SPONSOR_TIMEOUT,88 CUSTOM_DATA_LIMIT,89 CollectionLimits,90 CreateCollectionData,91 SponsorshipState,92 CreateItemExData,93 SponsoringRateLimit,94 budget::Budget,95 PhantomType,96 Property,97 Properties,98 PropertiesPermissionMap,99 PropertyKey,100 PropertyValue,101 PropertyPermission,102 PropertiesError,103 PropertyKeyPermission,104 TokenData,105 TrySetProperty,106 PropertyScope,107 // RMRK108 RmrkCollectionInfo,109 RmrkInstanceInfo,110 RmrkResourceInfo,111 RmrkPropertyInfo,112 RmrkBaseInfo,113 RmrkPartType,114 RmrkBoundedTheme,115 RmrkNftChild,116 CollectionPermissions,117};118use up_pov_estimate_rpc::PovInfo;119120pub use pallet::*;121use sp_core::H160;122use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};123#[cfg(feature = "runtime-benchmarks")]124pub mod benchmarking;125pub mod dispatch;126pub mod erc;127pub mod eth;128pub mod weights;129130/// Weight info.131pub type SelfWeightOf<T> = <T as Config>::WeightInfo;132133/// Collection handle contains information about collection data and id.134/// Also provides functionality to count consumed gas.135///136/// CollectionHandle is used as a generic wrapper for collections of all types.137/// It allows to perform common operations and queries on any collection type,138/// both completely general for all, as well as their respective implementations of [`CommonCollectionOperations`].139#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]140pub struct CollectionHandle<T: Config> {141 /// Collection id142 pub id: CollectionId,143 collection: Collection<T::AccountId>,144 /// Substrate recorder for counting consumed gas145 pub recorder: SubstrateRecorder<T>,146}147148impl<T: Config> WithRecorder<T> for CollectionHandle<T> {149 fn recorder(&self) -> &SubstrateRecorder<T> {150 &self.recorder151 }152 fn into_recorder(self) -> SubstrateRecorder<T> {153 self.recorder154 }155}156157impl<T: Config> CollectionHandle<T> {158 /// Same as [CollectionHandle::new] but with an explicit gas limit.159 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {160 <CollectionById<T>>::get(id).map(|collection| Self {161 id,162 collection,163 recorder: SubstrateRecorder::new(gas_limit),164 })165 }166167 /// Same as [CollectionHandle::new] but with an existed [`SubstrateRecorder`].168 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {169 <CollectionById<T>>::get(id).map(|collection| Self {170 id,171 collection,172 recorder,173 })174 }175176 /// Retrives collection data from storage and creates collection handle with default parameters.177 /// If collection not found return `None`178 pub fn new(id: CollectionId) -> Option<Self> {179 Self::new_with_gas_limit(id, u64::MAX)180 }181182 /// Same as [`CollectionHandle::new`] but if collection not found [CollectionNotFound](Error::CollectionNotFound) returned.183 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {184 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)185 }186187 /// Consume gas for reading.188 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 <T as frame_system::Config>::DbWeight::get()192 .read193 .saturating_mul(reads),194 )))195 }196197 /// Consume gas for writing.198 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {199 self.recorder200 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(201 <T as frame_system::Config>::DbWeight::get()202 .write203 .saturating_mul(writes),204 )))205 }206207 /// Consume gas for reading and writing.208 pub fn consume_store_reads_and_writes(209 &self,210 reads: u64,211 writes: u64,212 ) -> evm_coder::execution::Result<()> {213 let weight = <T as frame_system::Config>::DbWeight::get();214 let reads = weight.read.saturating_mul(reads);215 let writes = weight.read.saturating_mul(writes);216 self.recorder217 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(218 reads.saturating_add(writes),219 )))220 }221222 /// Save collection to storage.223 pub fn save(&self) -> DispatchResult {224 <CollectionById<T>>::insert(self.id, &self.collection);225 Ok(())226 }227228 /// Set collection sponsor.229 ///230 /// Unique collections allows sponsoring for certain actions.231 /// This method allows you to set the sponsor of the collection.232 /// In order for sponsorship to become active, it must be confirmed through [`Self::confirm_sponsorship`].233 pub fn set_sponsor(234 &mut self,235 sender: &T::CrossAccountId,236 sponsor: T::AccountId,237 ) -> DispatchResult {238 self.check_is_internal()?;239 self.check_is_owner_or_admin(sender)?;240241 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());242243 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));244 <PalletEvm<T>>::deposit_log(245 erc::CollectionHelpersEvents::CollectionChanged {246 collection_id: eth::collection_id_to_address(self.id),247 }248 .to_log(T::ContractAddress::get()),249 );250251 self.save()252 }253254 /// Force set `sponsor`.255 ///256 /// Differs from [`set_sponsor`][`Self::set_sponsor`] in that confirmation257 /// from the `sponsor` is not required.258 ///259 /// # Arguments260 ///261 /// * `sender`: Caller's account.262 /// * `sponsor`: ID of the account of the sponsor-to-be.263 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {264 self.check_is_internal()?;265266 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());267268 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));269 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));270 <PalletEvm<T>>::deposit_log(271 erc::CollectionHelpersEvents::CollectionChanged {272 collection_id: eth::collection_id_to_address(self.id),273 }274 .to_log(T::ContractAddress::get()),275 );276277 self.save()278 }279280 /// Confirm sponsorship281 ///282 /// In order for the sponsorship to become active, the user set as the sponsor must confirm their participation.283 /// Before confirming sponsorship, the user must be specified as the sponsor of the collection via [`Self::set_sponsor`].284 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {285 self.check_is_internal()?;286 ensure!(287 self.collection.sponsorship.pending_sponsor() == Some(sender),288 Error::<T>::ConfirmSponsorshipFail289 );290291 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());292293 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));294 <PalletEvm<T>>::deposit_log(295 erc::CollectionHelpersEvents::CollectionChanged {296 collection_id: eth::collection_id_to_address(self.id),297 }298 .to_log(T::ContractAddress::get()),299 );300301 self.save()302 }303304 /// Remove collection sponsor.305 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {306 self.check_is_internal()?;307 self.check_is_owner_or_admin(sender)?;308309 self.collection.sponsorship = SponsorshipState::Disabled;310311 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));312 <PalletEvm<T>>::deposit_log(313 erc::CollectionHelpersEvents::CollectionChanged {314 collection_id: eth::collection_id_to_address(self.id),315 }316 .to_log(T::ContractAddress::get()),317 );318 self.save()319 }320321 /// Force remove `sponsor`.322 ///323 /// Differs from `remove_sponsor` in that324 /// it doesn't require consent from the `owner` of the collection.325 pub fn force_remove_sponsor(&mut self) -> DispatchResult {326 self.check_is_internal()?;327328 self.collection.sponsorship = SponsorshipState::Disabled;329330 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));331 <PalletEvm<T>>::deposit_log(332 erc::CollectionHelpersEvents::CollectionChanged {333 collection_id: eth::collection_id_to_address(self.id),334 }335 .to_log(T::ContractAddress::get()),336 );337 self.save()338 }339340 /// Checks that the collection was created with, and must be operated upon through **Unique API**.341 /// Now check only the `external` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error.342 pub fn check_is_internal(&self) -> DispatchResult {343 if self.flags.external {344 return Err(<Error<T>>::CollectionIsExternal)?;345 }346347 Ok(())348 }349350 /// Checks that the collection was created with, and must be operated upon through an **assimilated API**.351 /// Now check only the `external` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error.352 pub fn check_is_external(&self) -> DispatchResult {353 if !self.flags.external {354 return Err(<Error<T>>::CollectionIsInternal)?;355 }356357 Ok(())358 }359}360361impl<T: Config> Deref for CollectionHandle<T> {362 type Target = Collection<T::AccountId>;363364 fn deref(&self) -> &Self::Target {365 &self.collection366 }367}368369impl<T: Config> DerefMut for CollectionHandle<T> {370 fn deref_mut(&mut self) -> &mut Self::Target {371 &mut self.collection372 }373}374375impl<T: Config> CollectionHandle<T> {376 /// Checks if the `user` is the owner of the collection.377 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {378 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);379 Ok(())380 }381382 /// Returns **true** if the `user` is the owner or administrator of the collection.383 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {384 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))385 }386387 /// Checks if the `user` is the owner or administrator of the collection.388 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {389 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);390 Ok(())391 }392393 /// Returns **true** if394 /// * the `user`is a collection owner or admin395 /// * the collection limits allow the owner/admins to transfer/burn any collection token396 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {397 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)398 }399400 /// Return **true** if `user` does not have enough token parts, and he can ignore such restrictions.401 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {402 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)403 }404405 /// Checks if the user is in the allow list. If not [Error::AddressNotInAllowlist] returns.406 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {407 ensure!(408 <Allowlist<T>>::get((self.id, user)),409 <Error<T>>::AddressNotInAllowlist410 );411 Ok(())412 }413414 /// Changes collection owner to another account415 /// #### Store read/writes416 /// 1 writes417 pub fn change_owner(418 &mut self,419 caller: T::CrossAccountId,420 new_owner: T::CrossAccountId,421 ) -> DispatchResult {422 self.check_is_internal()?;423 self.check_is_owner(&caller)?;424 self.collection.owner = new_owner.as_sub().clone();425426 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(427 self.id,428 new_owner.as_sub().clone(),429 ));430 <PalletEvm<T>>::deposit_log(431 erc::CollectionHelpersEvents::CollectionChanged {432 collection_id: eth::collection_id_to_address(self.id),433 }434 .to_log(T::ContractAddress::get()),435 );436437 self.save()438 }439}440441#[frame_support::pallet]442pub mod pallet {443 use super::*;444 use dispatch::CollectionDispatch;445 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};446 use frame_system::pallet_prelude::*;447 use frame_support::traits::Currency;448 use up_data_structs::{TokenId, mapping::TokenAddressMapping};449 use scale_info::TypeInfo;450 use weights::WeightInfo;451452 #[pallet::config]453 pub trait Config:454 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo455 {456 /// Weight information for functions of this pallet.457 type WeightInfo: WeightInfo;458459 /// Events compatible with [`frame_system::Config::Event`].460 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;461462 /// Handler of accounts and payment.463 type Currency: Currency<Self::AccountId>;464465 /// Set price to create a collection.466 #[pallet::constant]467 type CollectionCreationPrice: Get<468 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,469 >;470471 /// Dispatcher of operations on collections.472 type CollectionDispatch: CollectionDispatch<Self>;473474 /// Account which holds the chain's treasury.475 type TreasuryAccountId: Get<Self::AccountId>;476477 /// Address under which the CollectionHelper contract would be available.478 #[pallet::constant]479 type ContractAddress: Get<H160>;480481 /// Mapper for token addresses to Ethereum addresses.482 type EvmTokenAddressMapping: TokenAddressMapping<H160>;483484 /// Mapper for token addresses to [`CrossAccountId`].485 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;486 }487488 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);489490 #[pallet::pallet]491 #[pallet::storage_version(STORAGE_VERSION)]492 #[pallet::generate_store(pub(super) trait Store)]493 pub struct Pallet<T>(_);494495 #[pallet::extra_constants]496 impl<T: Config> Pallet<T> {497 /// Maximum admins per collection.498 pub fn collection_admins_limit() -> u32 {499 COLLECTION_ADMINS_LIMIT500 }501 }502503 #[pallet::event]504 #[pallet::generate_deposit(pub fn deposit_event)]505 pub enum Event<T: Config> {506 /// New collection was created507 CollectionCreated(508 /// Globally unique identifier of newly created collection.509 CollectionId,510 /// [`CollectionMode`] converted into _u8_.511 u8,512 /// Collection owner.513 T::AccountId,514 ),515516 /// New collection was destroyed517 CollectionDestroyed(518 /// Globally unique identifier of collection.519 CollectionId,520 ),521522 /// New item was created.523 ItemCreated(524 /// Id of the collection where item was created.525 CollectionId,526 /// Id of an item. Unique within the collection.527 TokenId,528 /// Owner of newly created item529 T::CrossAccountId,530 /// Always 1 for NFT531 u128,532 ),533534 /// Collection item was burned.535 ItemDestroyed(536 /// Id of the collection where item was destroyed.537 CollectionId,538 /// Identifier of burned NFT.539 TokenId,540 /// Which user has destroyed its tokens.541 T::CrossAccountId,542 /// Amount of token pieces destroed. Always 1 for NFT.543 u128,544 ),545546 /// Item was transferred547 Transfer(548 /// Id of collection to which item is belong.549 CollectionId,550 /// Id of an item.551 TokenId,552 /// Original owner of item.553 T::CrossAccountId,554 /// New owner of item.555 T::CrossAccountId,556 /// Amount of token pieces transfered. Always 1 for NFT.557 u128,558 ),559560 /// Amount pieces of token owned by `sender` was approved for `spender`.561 Approved(562 /// Id of collection to which item is belong.563 CollectionId,564 /// Id of an item.565 TokenId,566 /// Original owner of item.567 T::CrossAccountId,568 /// Id for which the approval was granted.569 T::CrossAccountId,570 /// Amount of token pieces transfered. Always 1 for NFT.571 u128,572 ),573574 /// A `sender` approves operations on all owned tokens for `spender`.575 ApprovedForAll(576 /// Id of collection to which item is belong.577 CollectionId,578 /// Owner of a wallet.579 T::CrossAccountId,580 /// Id for which operator status was granted or rewoked.581 T::CrossAccountId,582 /// Is operator status granted or revoked?583 bool,584 ),585586 /// The colletion property has been added or edited.587 CollectionPropertySet(588 /// Id of collection to which property has been set.589 CollectionId,590 /// The property that was set.591 PropertyKey,592 ),593594 /// The property has been deleted.595 CollectionPropertyDeleted(596 /// Id of collection to which property has been deleted.597 CollectionId,598 /// The property that was deleted.599 PropertyKey,600 ),601602 /// The token property has been added or edited.603 TokenPropertySet(604 /// Identifier of the collection whose token has the property set.605 CollectionId,606 /// The token for which the property was set.607 TokenId,608 /// The property that was set.609 PropertyKey,610 ),611612 /// The token property has been deleted.613 TokenPropertyDeleted(614 /// Identifier of the collection whose token has the property deleted.615 CollectionId,616 /// The token for which the property was deleted.617 TokenId,618 /// The property that was deleted.619 PropertyKey,620 ),621622 /// The token property permission of a collection has been set.623 PropertyPermissionSet(624 /// ID of collection to which property permission has been set.625 CollectionId,626 /// The property permission that was set.627 PropertyKey,628 ),629630 /// Address was added to the allow list.631 AllowListAddressAdded(632 /// ID of the affected collection.633 CollectionId,634 /// Address of the added account.635 T::CrossAccountId,636 ),637638 /// Address was removed from the allow list.639 AllowListAddressRemoved(640 /// ID of the affected collection.641 CollectionId,642 /// Address of the removed account.643 T::CrossAccountId,644 ),645646 /// Collection admin was added.647 CollectionAdminAdded(648 /// ID of the affected collection.649 CollectionId,650 /// Admin address.651 T::CrossAccountId,652 ),653654 /// Collection admin was removed.655 CollectionAdminRemoved(656 /// ID of the affected collection.657 CollectionId,658 /// Removed admin address.659 T::CrossAccountId,660 ),661662 /// Collection limits were set.663 CollectionLimitSet(664 /// ID of the affected collection.665 CollectionId,666 ),667668 /// Collection owned was changed.669 CollectionOwnerChanged(670 /// ID of the affected collection.671 CollectionId,672 /// New owner address.673 T::AccountId,674 ),675676 /// Collection permissions were set.677 CollectionPermissionSet(678 /// ID of the affected collection.679 CollectionId,680 ),681682 /// Collection sponsor was set.683 CollectionSponsorSet(684 /// ID of the affected collection.685 CollectionId,686 /// New sponsor address.687 T::AccountId,688 ),689690 /// New sponsor was confirm.691 SponsorshipConfirmed(692 /// ID of the affected collection.693 CollectionId,694 /// New sponsor address.695 T::AccountId,696 ),697698 /// Collection sponsor was removed.699 CollectionSponsorRemoved(700 /// ID of the affected collection.701 CollectionId,702 ),703 }704705 #[pallet::error]706 pub enum Error<T> {707 /// This collection does not exist.708 CollectionNotFound,709 /// Sender parameter and item owner must be equal.710 MustBeTokenOwner,711 /// No permission to perform action712 NoPermission,713 /// Destroying only empty collections is allowed714 CantDestroyNotEmptyCollection,715 /// Collection is not in mint mode.716 PublicMintingNotAllowed,717 /// Address is not in allow list.718 AddressNotInAllowlist,719720 /// Collection name can not be longer than 63 char.721 CollectionNameLimitExceeded,722 /// Collection description can not be longer than 255 char.723 CollectionDescriptionLimitExceeded,724 /// Token prefix can not be longer than 15 char.725 CollectionTokenPrefixLimitExceeded,726 /// Total collections bound exceeded.727 TotalCollectionsLimitExceeded,728 /// Exceeded max admin count729 CollectionAdminCountExceeded,730 /// Collection limit bounds per collection exceeded731 CollectionLimitBoundsExceeded,732 /// Tried to enable permissions which are only permitted to be disabled733 OwnerPermissionsCantBeReverted,734 /// Collection settings not allowing items transferring735 TransferNotAllowed,736 /// Account token limit exceeded per collection737 AccountTokenLimitExceeded,738 /// Collection token limit exceeded739 CollectionTokenLimitExceeded,740 /// Metadata flag frozen741 MetadataFlagFrozen,742743 /// Item does not exist744 TokenNotFound,745 /// Item is balance not enough746 TokenValueTooLow,747 /// Requested value is more than the approved748 ApprovedValueTooLow,749 /// Tried to approve more than owned750 CantApproveMoreThanOwned,751 /// Only spending from eth mirror could be approved752 AddressIsNotEthMirror,753754 /// Can't transfer tokens to ethereum zero address755 AddressIsZero,756757 /// The operation is not supported758 UnsupportedOperation,759760 /// Insufficient funds to perform an action761 NotSufficientFounds,762763 /// User does not satisfy the nesting rule764 UserIsNotAllowedToNest,765 /// Only tokens from specific collections may nest tokens under this one766 SourceCollectionIsNotAllowedToNest,767768 /// Tried to store more data than allowed in collection field769 CollectionFieldSizeExceeded,770771 /// Tried to store more property data than allowed772 NoSpaceForProperty,773774 /// Tried to store more property keys than allowed775 PropertyLimitReached,776777 /// Property key is too long778 PropertyKeyIsTooLong,779780 /// Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed781 InvalidCharacterInPropertyKey,782783 /// Empty property keys are forbidden784 EmptyPropertyKey,785786 /// Tried to access an external collection with an internal API787 CollectionIsExternal,788789 /// Tried to access an internal collection with an external API790 CollectionIsInternal,791792 /// This address is not set as sponsor, use setCollectionSponsor first.793 ConfirmSponsorshipFail,794795 /// The user is not an administrator.796 UserIsNotCollectionAdmin,797 }798799 /// Storage of the count of created collections. Essentially contains the last collection ID.800 #[pallet::storage]801 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;802803 /// Storage of the count of deleted collections.804 #[pallet::storage]805 pub type DestroyedCollectionCount<T> =806 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;807808 /// Storage of collection info.809 #[pallet::storage]810 pub type CollectionById<T> = StorageMap<811 Hasher = Blake2_128Concat,812 Key = CollectionId,813 Value = Collection<<T as frame_system::Config>::AccountId>,814 QueryKind = OptionQuery,815 >;816817 /// Storage of collection properties.818 #[pallet::storage]819 #[pallet::getter(fn collection_properties)]820 pub type CollectionProperties<T> = StorageMap<821 Hasher = Blake2_128Concat,822 Key = CollectionId,823 Value = Properties,824 QueryKind = ValueQuery,825 OnEmpty = up_data_structs::CollectionProperties,826 >;827828 /// Storage of token property permissions of a collection.829 #[pallet::storage]830 #[pallet::getter(fn property_permissions)]831 pub type CollectionPropertyPermissions<T> = StorageMap<832 Hasher = Blake2_128Concat,833 Key = CollectionId,834 Value = PropertiesPermissionMap,835 QueryKind = ValueQuery,836 >;837838 /// Storage of the amount of collection admins.839 #[pallet::storage]840 pub type AdminAmount<T> = StorageMap<841 Hasher = Blake2_128Concat,842 Key = CollectionId,843 Value = u32,844 QueryKind = ValueQuery,845 >;846847 /// List of collection admins.848 #[pallet::storage]849 pub type IsAdmin<T: Config> = StorageNMap<850 Key = (851 Key<Blake2_128Concat, CollectionId>,852 Key<Blake2_128Concat, T::CrossAccountId>,853 ),854 Value = bool,855 QueryKind = ValueQuery,856 >;857858 /// Allowlisted collection users.859 #[pallet::storage]860 pub type Allowlist<T: Config> = StorageNMap<861 Key = (862 Key<Blake2_128Concat, CollectionId>,863 Key<Blake2_128Concat, T::CrossAccountId>,864 ),865 Value = bool,866 QueryKind = ValueQuery,867 >;868869 /// Not used by code, exists only to provide some types to metadata.870 #[pallet::storage]871 pub type DummyStorageValue<T: Config> = StorageValue<872 Value = (873 CollectionStats,874 CollectionId,875 TokenId,876 TokenChild,877 PhantomType<(878 TokenData<T::CrossAccountId>,879 RpcCollection<T::AccountId>,880 // RMRK881 RmrkCollectionInfo<T::AccountId>,882 RmrkInstanceInfo<T::AccountId>,883 RmrkResourceInfo,884 RmrkPropertyInfo,885 RmrkBaseInfo<T::AccountId>,886 RmrkPartType,887 RmrkBoundedTheme,888 RmrkNftChild,889 // PoV Estimate Info890 PovInfo,891 )>,892 ),893 QueryKind = OptionQuery,894 >;895896 #[pallet::hooks]897 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {898 fn on_runtime_upgrade() -> Weight {899 StorageVersion::new(1).put::<Pallet<T>>();900901 Weight::zero()902 }903 }904}905906impl<T: Config> Pallet<T> {907 /// Enshure that receiver address is correct.908 ///909 /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens.910 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {911 ensure!(912 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,913 <Error<T>>::AddressIsZero914 );915 Ok(())916 }917918 /// Get a vector of collection admins.919 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {920 <IsAdmin<T>>::iter_prefix((collection,))921 .map(|(a, _)| a)922 .collect()923 }924925 /// Get a vector of users allowed to mint tokens.926 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {927 <Allowlist<T>>::iter_prefix((collection,))928 .map(|(a, _)| a)929 .collect()930 }931932 /// Is `user` allowed to mint token in `collection`.933 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {934 <Allowlist<T>>::get((collection, user))935 }936937 /// Get statistics of collections.938 pub fn collection_stats() -> CollectionStats {939 let created = <CreatedCollectionCount<T>>::get();940 let destroyed = <DestroyedCollectionCount<T>>::get();941 CollectionStats {942 created: created.0,943 destroyed: destroyed.0,944 alive: created.0 - destroyed.0,945 }946 }947948 /// Get the effective limits for the collection.949 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {950 let collection = <CollectionById<T>>::get(collection)?;951 let limits = collection.limits;952 let effective_limits = CollectionLimits {953 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),954 sponsored_data_size: Some(limits.sponsored_data_size()),955 sponsored_data_rate_limit: Some(956 limits957 .sponsored_data_rate_limit958 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),959 ),960 token_limit: Some(limits.token_limit()),961 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(962 match collection.mode {963 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,964 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,965 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,966 },967 )),968 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),969 owner_can_transfer: Some(limits.owner_can_transfer()),970 owner_can_destroy: Some(limits.owner_can_destroy()),971 transfers_enabled: Some(limits.transfers_enabled()),972 };973974 Some(effective_limits)975 }976977 /// Returns information about the `collection` adapted for rpc.978 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {979 let Collection {980 name,981 description,982 owner,983 mode,984 token_prefix,985 sponsorship,986 limits,987 permissions,988 flags,989 } = <CollectionById<T>>::get(collection)?;990991 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)992 .into_iter()993 .map(|(key, permission)| PropertyKeyPermission { key, permission })994 .collect();995996 let properties = <CollectionProperties<T>>::get(collection)997 .into_iter()998 .map(|(key, value)| Property { key, value })999 .collect();10001001 let permissions = CollectionPermissions {1002 access: Some(permissions.access()),1003 mint_mode: Some(permissions.mint_mode()),1004 nesting: Some(permissions.nesting().clone()),1005 };10061007 Some(RpcCollection {1008 name: name.into_inner(),1009 description: description.into_inner(),1010 owner,1011 mode,1012 token_prefix: token_prefix.into_inner(),1013 sponsorship,1014 limits,1015 permissions,1016 token_property_permissions,1017 properties,1018 read_only: flags.external,10191020 flags: RpcCollectionFlags {1021 foreign: flags.foreign,1022 erc721metadata: flags.erc721metadata,1023 },1024 })1025 }1026}10271028macro_rules! limit_default {1029 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1030 $(1031 if let Some($new) = $new.$field {1032 let $old = $old.$field($($arg)?);1033 let _ = $new;1034 let _ = $old;1035 $check1036 } else {1037 $new.$field = $old.$field1038 }1039 )*1040 }};1041}1042macro_rules! limit_default_clone {1043 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1044 $(1045 if let Some($new) = $new.$field.clone() {1046 let $old = $old.$field($($arg)?);1047 let _ = $new;1048 let _ = $old;1049 $check1050 } else {1051 $new.$field = $old.$field.clone()1052 }1053 )*1054 }};1055}10561057impl<T: Config> Pallet<T> {1058 /// Create new collection.1059 ///1060 /// * `owner` - The owner of the collection.1061 /// * `data` - Description of the created collection.1062 /// * `flags` - Extra flags to store.1063 pub fn init_collection(1064 owner: T::CrossAccountId,1065 payer: T::CrossAccountId,1066 data: CreateCollectionData<T::AccountId>,1067 flags: CollectionFlags,1068 ) -> Result<CollectionId, DispatchError> {1069 {1070 ensure!(1071 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1072 Error::<T>::CollectionTokenPrefixLimitExceeded1073 );1074 }10751076 let created_count = <CreatedCollectionCount<T>>::get()1077 .01078 .checked_add(1)1079 .ok_or(ArithmeticError::Overflow)?;1080 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1081 let id = CollectionId(created_count);10821083 // bound Total number of collections1084 ensure!(1085 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1086 <Error<T>>::TotalCollectionsLimitExceeded1087 );10881089 // =========10901091 let collection = Collection {1092 owner: owner.as_sub().clone(),1093 name: data.name,1094 mode: data.mode.clone(),1095 description: data.description,1096 token_prefix: data.token_prefix,1097 sponsorship: data1098 .pending_sponsor1099 .map(SponsorshipState::Unconfirmed)1100 .unwrap_or_default(),1101 limits: data1102 .limits1103 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1104 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1105 permissions: data1106 .permissions1107 .map(|permissions| {1108 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1109 })1110 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1111 flags,1112 };11131114 let mut collection_properties = up_data_structs::CollectionProperties::get();1115 collection_properties1116 .try_set_from_iter(data.properties.into_iter())1117 .map_err(<Error<T>>::from)?;11181119 CollectionProperties::<T>::insert(id, collection_properties);11201121 let mut token_props_permissions = PropertiesPermissionMap::new();1122 token_props_permissions1123 .try_set_from_iter(data.token_property_permissions.into_iter())1124 .map_err(<Error<T>>::from)?;11251126 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11271128 // Take a (non-refundable) deposit of collection creation1129 {1130 let mut imbalance =1131 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1132 imbalance.subsume(1133 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1134 &T::TreasuryAccountId::get(),1135 T::CollectionCreationPrice::get(),1136 ),1137 );1138 <T as Config>::Currency::settle(1139 payer.as_sub(),1140 imbalance,1141 WithdrawReasons::TRANSFER,1142 ExistenceRequirement::KeepAlive,1143 )1144 .map_err(|_| Error::<T>::NotSufficientFounds)?;1145 }11461147 <CreatedCollectionCount<T>>::put(created_count);1148 <Pallet<T>>::deposit_event(Event::CollectionCreated(1149 id,1150 data.mode.id(),1151 owner.as_sub().clone(),1152 ));1153 <PalletEvm<T>>::deposit_log(1154 erc::CollectionHelpersEvents::CollectionCreated {1155 owner: *owner.as_eth(),1156 collection_id: eth::collection_id_to_address(id),1157 }1158 .to_log(T::ContractAddress::get()),1159 );1160 <CollectionById<T>>::insert(id, collection);1161 Ok(id)1162 }11631164 /// Destroy collection.1165 ///1166 /// * `collection` - Collection handler.1167 /// * `sender` - The owner or administrator of the collection.1168 pub fn destroy_collection(1169 collection: CollectionHandle<T>,1170 sender: &T::CrossAccountId,1171 ) -> DispatchResult {1172 ensure!(1173 collection.limits.owner_can_destroy(),1174 <Error<T>>::NoPermission,1175 );1176 collection.check_is_owner(sender)?;11771178 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1179 .01180 .checked_add(1)1181 .ok_or(ArithmeticError::Overflow)?;11821183 // =========11841185 <DestroyedCollectionCount<T>>::put(destroyed_collections);1186 <CollectionById<T>>::remove(collection.id);1187 <AdminAmount<T>>::remove(collection.id);1188 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1189 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1190 <CollectionProperties<T>>::remove(collection.id);11911192 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));11931194 <PalletEvm<T>>::deposit_log(1195 erc::CollectionHelpersEvents::CollectionDestroyed {1196 collection_id: eth::collection_id_to_address(collection.id),1197 }1198 .to_log(T::ContractAddress::get()),1199 );1200 Ok(())1201 }12021203 /// This function sets or removes a collection properties according to1204 /// `properties_updates` contents:1205 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`1206 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.1207 ///1208 /// This function fires an event for each property change.1209 /// In case of an error, all the changes (including the events) will be reverted1210 /// since the function is transactional.1211 #[transactional]1212 fn modify_collection_properties(1213 collection: &CollectionHandle<T>,1214 sender: &T::CrossAccountId,1215 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1216 ) -> DispatchResult {1217 collection.check_is_owner_or_admin(sender)?;12181219 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12201221 for (key, value) in properties_updates {1222 match value {1223 Some(value) => {1224 stored_properties1225 .try_set(key.clone(), value)1226 .map_err(<Error<T>>::from)?;12271228 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1229 <PalletEvm<T>>::deposit_log(1230 erc::CollectionHelpersEvents::CollectionChanged {1231 collection_id: eth::collection_id_to_address(collection.id),1232 }1233 .to_log(T::ContractAddress::get()),1234 );1235 }1236 None => {1237 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12381239 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1240 <PalletEvm<T>>::deposit_log(1241 erc::CollectionHelpersEvents::CollectionChanged {1242 collection_id: eth::collection_id_to_address(collection.id),1243 }1244 .to_log(T::ContractAddress::get()),1245 );1246 }1247 }1248 }12491250 <CollectionProperties<T>>::set(collection.id, stored_properties);12511252 Ok(())1253 }12541255 /// Set collection property.1256 ///1257 /// * `collection` - Collection handler.1258 /// * `sender` - The owner or administrator of the collection.1259 /// * `property` - The property to set.1260 pub fn set_collection_property(1261 collection: &CollectionHandle<T>,1262 sender: &T::CrossAccountId,1263 property: Property,1264 ) -> DispatchResult {1265 Self::set_collection_properties(collection, sender, [property].into_iter())1266 }12671268 /// Set a scoped collection property, where the scope is a special prefix1269 /// prohibiting a user access to change the property directly.1270 ///1271 /// * `collection_id` - ID of the collection for which the property is being set.1272 /// * `scope` - Property scope.1273 /// * `property` - The property to set.1274 pub fn set_scoped_collection_property(1275 collection_id: CollectionId,1276 scope: PropertyScope,1277 property: Property,1278 ) -> DispatchResult {1279 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1280 properties.try_scoped_set(scope, property.key, property.value)1281 })1282 .map_err(<Error<T>>::from)?;12831284 Ok(())1285 }12861287 /// Set scoped collection properties, where the scope is a special prefix1288 /// prohibiting a user access to change the properties directly.1289 ///1290 /// * `collection_id` - ID of the collection for which the properties is being set.1291 /// * `scope` - Property scope.1292 /// * `properties` - The properties to set.1293 pub fn set_scoped_collection_properties(1294 collection_id: CollectionId,1295 scope: PropertyScope,1296 properties: impl Iterator<Item = Property>,1297 ) -> DispatchResult {1298 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1299 stored_properties.try_scoped_set_from_iter(scope, properties)1300 })1301 .map_err(<Error<T>>::from)?;13021303 Ok(())1304 }13051306 /// Set collection properties.1307 ///1308 /// * `collection` - Collection handler.1309 /// * `sender` - The owner or administrator of the collection.1310 /// * `properties` - The properties to set.1311 pub fn set_collection_properties(1312 collection: &CollectionHandle<T>,1313 sender: &T::CrossAccountId,1314 properties: impl Iterator<Item = Property>,1315 ) -> DispatchResult {1316 Self::modify_collection_properties(1317 collection,1318 sender,1319 properties.map(|property| (property.key, Some(property.value))),1320 )1321 }13221323 /// Delete collection property.1324 ///1325 /// * `collection` - Collection handler.1326 /// * `sender` - The owner or administrator of the collection.1327 /// * `property` - The property to delete.1328 pub fn delete_collection_property(1329 collection: &CollectionHandle<T>,1330 sender: &T::CrossAccountId,1331 property_key: PropertyKey,1332 ) -> DispatchResult {1333 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1334 }13351336 /// Delete collection properties.1337 ///1338 /// * `collection` - Collection handler.1339 /// * `sender` - The owner or administrator of the collection.1340 /// * `properties` - The properties to delete.1341 pub fn delete_collection_properties(1342 collection: &CollectionHandle<T>,1343 sender: &T::CrossAccountId,1344 property_keys: impl Iterator<Item = PropertyKey>,1345 ) -> DispatchResult {1346 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1347 }13481349 /// Set collection propetry permission without any checks.1350 ///1351 /// Used for migrations.1352 ///1353 /// * `collection` - Collection handler.1354 /// * `property_permissions` - Property permissions.1355 pub fn set_property_permission_unchecked(1356 collection: CollectionId,1357 property_permission: PropertyKeyPermission,1358 ) -> DispatchResult {1359 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1360 permissions.try_set(property_permission.key, property_permission.permission)1361 })1362 .map_err(<Error<T>>::from)?;1363 Ok(())1364 }13651366 /// Set collection property permission.1367 ///1368 /// * `collection` - Collection handler.1369 /// * `sender` - The owner or administrator of the collection.1370 /// * `property_permission` - Property permission.1371 pub fn set_property_permission(1372 collection: &CollectionHandle<T>,1373 sender: &T::CrossAccountId,1374 property_permission: PropertyKeyPermission,1375 ) -> DispatchResult {1376 Self::set_scoped_property_permission(1377 collection,1378 sender,1379 PropertyScope::None,1380 property_permission,1381 )1382 }13831384 /// Set collection property permission with scope.1385 ///1386 /// * `collection` - Collection handler.1387 /// * `sender` - The owner or administrator of the collection.1388 /// * `scope` - Property scope.1389 /// * `property_permission` - Property permission.1390 pub fn set_scoped_property_permission(1391 collection: &CollectionHandle<T>,1392 sender: &T::CrossAccountId,1393 scope: PropertyScope,1394 property_permission: PropertyKeyPermission,1395 ) -> DispatchResult {1396 collection.check_is_owner_or_admin(sender)?;13971398 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1399 let current_permission = all_permissions.get(&property_permission.key);1400 if matches![1401 current_permission,1402 Some(PropertyPermission { mutable: false, .. })1403 ] {1404 return Err(<Error<T>>::NoPermission.into());1405 }14061407 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1408 let property_permission = property_permission.clone();1409 permissions.try_scoped_set(1410 scope,1411 property_permission.key,1412 property_permission.permission,1413 )1414 })1415 .map_err(<Error<T>>::from)?;14161417 Self::deposit_event(Event::PropertyPermissionSet(1418 collection.id,1419 property_permission.key,1420 ));1421 <PalletEvm<T>>::deposit_log(1422 erc::CollectionHelpersEvents::CollectionChanged {1423 collection_id: eth::collection_id_to_address(collection.id),1424 }1425 .to_log(T::ContractAddress::get()),1426 );14271428 Ok(())1429 }14301431 /// Set token property permission.1432 ///1433 /// * `collection` - Collection handler.1434 /// * `sender` - The owner or administrator of the collection.1435 /// * `property_permissions` - Property permissions.1436 #[transactional]1437 pub fn set_token_property_permissions(1438 collection: &CollectionHandle<T>,1439 sender: &T::CrossAccountId,1440 property_permissions: Vec<PropertyKeyPermission>,1441 ) -> DispatchResult {1442 Self::set_scoped_token_property_permissions(1443 collection,1444 sender,1445 PropertyScope::None,1446 property_permissions,1447 )1448 }14491450 /// Set token property permission with scope.1451 ///1452 /// * `collection` - Collection handler.1453 /// * `sender` - The owner or administrator of the collection.1454 /// * `scope` - Property scope.1455 /// * `property_permissions` - Property permissions.1456 #[transactional]1457 pub fn set_scoped_token_property_permissions(1458 collection: &CollectionHandle<T>,1459 sender: &T::CrossAccountId,1460 scope: PropertyScope,1461 property_permissions: Vec<PropertyKeyPermission>,1462 ) -> DispatchResult {1463 for prop_pemission in property_permissions {1464 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1465 }14661467 Ok(())1468 }14691470 /// Get collection property.1471 pub fn get_collection_property(1472 collection_id: CollectionId,1473 key: &PropertyKey,1474 ) -> Option<PropertyValue> {1475 Self::collection_properties(collection_id).get(key).cloned()1476 }14771478 /// Convert byte vector to property key vector.1479 pub fn bytes_keys_to_property_keys(1480 keys: Vec<Vec<u8>>,1481 ) -> Result<Vec<PropertyKey>, DispatchError> {1482 keys.into_iter()1483 .map(|key| -> Result<PropertyKey, DispatchError> {1484 key.try_into()1485 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1486 })1487 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1488 }14891490 /// Get properties according to given keys.1491 pub fn filter_collection_properties(1492 collection_id: CollectionId,1493 keys: Option<Vec<PropertyKey>>,1494 ) -> Result<Vec<Property>, DispatchError> {1495 let properties = Self::collection_properties(collection_id);14961497 let properties = keys1498 .map(|keys| {1499 keys.into_iter()1500 .filter_map(|key| {1501 properties.get(&key).map(|value| Property {1502 key,1503 value: value.clone(),1504 })1505 })1506 .collect()1507 })1508 .unwrap_or_else(|| {1509 properties1510 .into_iter()1511 .map(|(key, value)| Property { key, value })1512 .collect()1513 });15141515 Ok(properties)1516 }15171518 /// Get property permissions according to given keys.1519 pub fn filter_property_permissions(1520 collection_id: CollectionId,1521 keys: Option<Vec<PropertyKey>>,1522 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1523 let permissions = Self::property_permissions(collection_id);15241525 let key_permissions = keys1526 .map(|keys| {1527 keys.into_iter()1528 .filter_map(|key| {1529 permissions1530 .get(&key)1531 .map(|permission| PropertyKeyPermission {1532 key,1533 permission: permission.clone(),1534 })1535 })1536 .collect()1537 })1538 .unwrap_or_else(|| {1539 permissions1540 .into_iter()1541 .map(|(key, permission)| PropertyKeyPermission { key, permission })1542 .collect()1543 });15441545 Ok(key_permissions)1546 }15471548 /// Toggle `user` participation in the `collection`'s allow list.1549 /// #### Store read/writes1550 /// 1 writes1551 pub fn toggle_allowlist(1552 collection: &CollectionHandle<T>,1553 sender: &T::CrossAccountId,1554 user: &T::CrossAccountId,1555 allowed: bool,1556 ) -> DispatchResult {1557 collection.check_is_owner_or_admin(sender)?;15581559 // =========15601561 if allowed {1562 <Allowlist<T>>::insert((collection.id, user), true);1563 Self::deposit_event(Event::<T>::AllowListAddressAdded(1564 collection.id,1565 user.clone(),1566 ));1567 } else {1568 <Allowlist<T>>::remove((collection.id, user));1569 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1570 collection.id,1571 user.clone(),1572 ));1573 }15741575 <PalletEvm<T>>::deposit_log(1576 erc::CollectionHelpersEvents::CollectionChanged {1577 collection_id: eth::collection_id_to_address(collection.id),1578 }1579 .to_log(T::ContractAddress::get()),1580 );15811582 Ok(())1583 }15841585 /// Toggle `user` participation in the `collection`'s admin list.1586 /// #### Store read/writes1587 /// 2 reads, 2 writes1588 pub fn toggle_admin(1589 collection: &CollectionHandle<T>,1590 sender: &T::CrossAccountId,1591 user: &T::CrossAccountId,1592 admin: bool,1593 ) -> DispatchResult {1594 collection.check_is_internal()?;1595 collection.check_is_owner(sender)?;15961597 let is_admin = <IsAdmin<T>>::get((collection.id, user));1598 if is_admin == admin {1599 if admin {1600 return Ok(());1601 } else {1602 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1603 }1604 }1605 let amount = <AdminAmount<T>>::get(collection.id);16061607 // =========16081609 if admin {1610 let amount = amount1611 .checked_add(1)1612 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1613 ensure!(1614 amount <= Self::collection_admins_limit(),1615 <Error<T>>::CollectionAdminCountExceeded,1616 );16171618 <AdminAmount<T>>::insert(collection.id, amount);1619 <IsAdmin<T>>::insert((collection.id, user), true);16201621 Self::deposit_event(Event::<T>::CollectionAdminAdded(1622 collection.id,1623 user.clone(),1624 ));1625 } else {1626 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1627 <IsAdmin<T>>::remove((collection.id, user));16281629 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1630 collection.id,1631 user.clone(),1632 ));1633 }16341635 <PalletEvm<T>>::deposit_log(1636 erc::CollectionHelpersEvents::CollectionChanged {1637 collection_id: eth::collection_id_to_address(collection.id),1638 }1639 .to_log(T::ContractAddress::get()),1640 );16411642 Ok(())1643 }16441645 /// Update collection limits.1646 pub fn update_limits(1647 user: &T::CrossAccountId,1648 collection: &mut CollectionHandle<T>,1649 new_limit: CollectionLimits,1650 ) -> DispatchResult {1651 collection.check_is_internal()?;1652 collection.check_is_owner_or_admin(user)?;16531654 collection.limits =1655 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16561657 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1658 <PalletEvm<T>>::deposit_log(1659 erc::CollectionHelpersEvents::CollectionChanged {1660 collection_id: eth::collection_id_to_address(collection.id),1661 }1662 .to_log(T::ContractAddress::get()),1663 );16641665 collection.save()1666 }16671668 /// Merge set fields from `new_limit` to `old_limit`.1669 fn clamp_limits(1670 mode: CollectionMode,1671 old_limit: &CollectionLimits,1672 mut new_limit: CollectionLimits,1673 ) -> Result<CollectionLimits, DispatchError> {1674 let limits = old_limit;1675 limit_default!(old_limit, new_limit,1676 account_token_ownership_limit => ensure!(1677 new_limit <= MAX_TOKEN_OWNERSHIP,1678 <Error<T>>::CollectionLimitBoundsExceeded,1679 ),1680 sponsored_data_size => ensure!(1681 new_limit <= CUSTOM_DATA_LIMIT,1682 <Error<T>>::CollectionLimitBoundsExceeded,1683 ),16841685 sponsored_data_rate_limit => {},1686 token_limit => ensure!(1687 old_limit >= new_limit && new_limit > 0,1688 <Error<T>>::CollectionTokenLimitExceeded1689 ),16901691 sponsor_transfer_timeout(match mode {1692 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1693 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1694 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1695 }) => ensure!(1696 new_limit <= MAX_SPONSOR_TIMEOUT,1697 <Error<T>>::CollectionLimitBoundsExceeded,1698 ),1699 sponsor_approve_timeout => {},1700 owner_can_transfer => ensure!(1701 !limits.owner_can_transfer_instaled() ||1702 old_limit || !new_limit,1703 <Error<T>>::OwnerPermissionsCantBeReverted,1704 ),1705 owner_can_destroy => ensure!(1706 old_limit || !new_limit,1707 <Error<T>>::OwnerPermissionsCantBeReverted,1708 ),1709 transfers_enabled => {},1710 );1711 Ok(new_limit)1712 }17131714 /// Update collection permissions.1715 pub fn update_permissions(1716 user: &T::CrossAccountId,1717 collection: &mut CollectionHandle<T>,1718 new_permission: CollectionPermissions,1719 ) -> DispatchResult {1720 collection.check_is_internal()?;1721 collection.check_is_owner_or_admin(user)?;1722 collection.permissions = Self::clamp_permissions(1723 collection.mode.clone(),1724 &collection.permissions,1725 new_permission,1726 )?;17271728 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1729 <PalletEvm<T>>::deposit_log(1730 erc::CollectionHelpersEvents::CollectionChanged {1731 collection_id: eth::collection_id_to_address(collection.id),1732 }1733 .to_log(T::ContractAddress::get()),1734 );17351736 collection.save()1737 }17381739 /// Merge set fields from `new_permission` to `old_permission`.1740 fn clamp_permissions(1741 _mode: CollectionMode,1742 old_permission: &CollectionPermissions,1743 mut new_permission: CollectionPermissions,1744 ) -> Result<CollectionPermissions, DispatchError> {1745 limit_default_clone!(old_permission, new_permission,1746 access => {},1747 mint_mode => {},1748 nesting => { /* todo check for permissive, if only it gets out of benchmarks */ },1749 );1750 Ok(new_permission)1751 }17521753 /// Repair possibly broken properties of a collection.1754 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1755 CollectionProperties::<T>::mutate(collection_id, |properties| {1756 properties.recompute_consumed_space();1757 });17581759 Ok(())1760 }1761}17621763/// Indicates unsupported methods by returning [Error::UnsupportedOperation].1764#[macro_export]1765macro_rules! unsupported {1766 ($runtime:path) => {1767 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1768 };1769}17701771/// Return weights for various worst-case operations.1772pub trait CommonWeightInfo<CrossAccountId> {1773 /// Weight of item creation.1774 fn create_item() -> Weight;17751776 /// Weight of items creation.1777 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17781779 /// Weight of items creation.1780 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17811782 /// The weight of the burning item.1783 fn burn_item() -> Weight;17841785 /// Property setting weight.1786 ///1787 /// * `amount`- The number of properties to set.1788 fn set_collection_properties(amount: u32) -> Weight;17891790 /// Collection property deletion weight.1791 ///1792 /// * `amount`- The number of properties to set.1793 fn delete_collection_properties(amount: u32) -> Weight;17941795 /// Token property setting weight.1796 ///1797 /// * `amount`- The number of properties to set.1798 fn set_token_properties(amount: u32) -> Weight;17991800 /// Token property deletion weight.1801 ///1802 /// * `amount`- The number of properties to delete.1803 fn delete_token_properties(amount: u32) -> Weight;18041805 /// Token property permissions set weight.1806 ///1807 /// * `amount`- The number of property permissions to set.1808 fn set_token_property_permissions(amount: u32) -> Weight;18091810 /// Transfer price of the token or its parts.1811 fn transfer() -> Weight;18121813 /// The price of setting the permission of the operation from another user.1814 fn approve() -> Weight;18151816 /// The price of setting the permission of the operation from another user for eth mirror.1817 fn approve_from() -> Weight;18181819 /// Transfer price from another user.1820 fn transfer_from() -> Weight;18211822 /// The price of burning a token from another user.1823 fn burn_from() -> Weight;18241825 /// Differs from burn_item in case of Fungible and Refungible, as it should burn1826 /// whole users's balance.1827 ///1828 /// This method shouldn't be used directly, as it doesn't count breadth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1829 fn burn_recursively_self_raw() -> Weight;18301831 /// Cost of iterating over `amount` children while burning, without counting child burning itself.1832 ///1833 /// This method shouldn't be used directly, as it doesn't count depth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1834 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18351836 /// The price of recursive burning a token.1837 ///1838 /// `max_selfs` - The maximum burning weight of the token itself.1839 /// `max_breadth` - The maximum number of nested tokens to burn.1840 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1841 Self::burn_recursively_self_raw()1842 .saturating_mul(max_selfs.max(1) as u64)1843 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1844 }18451846 /// The price of retrieving token owner1847 fn token_owner() -> Weight;18481849 /// The price of setting approval for all1850 fn set_allowance_for_all() -> Weight;18511852 /// The price of repairing an item.1853 fn force_repair_item() -> Weight;1854}18551856/// Weight info extension trait for refungible pallet.1857pub trait RefungibleExtensionsWeightInfo {1858 /// Weight of token repartition.1859 fn repartition() -> Weight;1860}18611862/// Common collection operations.1863///1864/// It wraps methods in Fungible, Nonfungible and Refungible pallets1865/// and adds weight info.1866pub trait CommonCollectionOperations<T: Config> {1867 /// Create token.1868 ///1869 /// * `sender` - The user who mint the token and pays for the transaction.1870 /// * `to` - The user who will own the token.1871 /// * `data` - Token data.1872 /// * `nesting_budget` - A budget that can be spent on nesting tokens.1873 fn create_item(1874 &self,1875 sender: T::CrossAccountId,1876 to: T::CrossAccountId,1877 data: CreateItemData,1878 nesting_budget: &dyn Budget,1879 ) -> DispatchResultWithPostInfo;18801881 /// Create multiple tokens.1882 ///1883 /// * `sender` - The user who mint the token and pays for the transaction.1884 /// * `to` - The user who will own the token.1885 /// * `data` - Token data.1886 /// * `nesting_budget` - A budget that can be spent on nesting tokens.1887 fn create_multiple_items(1888 &self,1889 sender: T::CrossAccountId,1890 to: T::CrossAccountId,1891 data: Vec<CreateItemData>,1892 nesting_budget: &dyn Budget,1893 ) -> DispatchResultWithPostInfo;18941895 /// Create multiple tokens.1896 ///1897 /// * `sender` - The user who mint the token and pays for the transaction.1898 /// * `to` - The user who will own the token.1899 /// * `data` - Token data.1900 /// * `nesting_budget` - A budget that can be spent on nesting tokens.1901 fn create_multiple_items_ex(1902 &self,1903 sender: T::CrossAccountId,1904 data: CreateItemExData<T::CrossAccountId>,1905 nesting_budget: &dyn Budget,1906 ) -> DispatchResultWithPostInfo;19071908 /// Burn token.1909 ///1910 /// * `sender` - The user who owns the token.1911 /// * `token` - Token id that will burned.1912 /// * `amount` - The number of parts of the token that will be burned.1913 fn burn_item(1914 &self,1915 sender: T::CrossAccountId,1916 token: TokenId,1917 amount: u128,1918 ) -> DispatchResultWithPostInfo;19191920 /// Burn token and all nested tokens recursievly.1921 ///1922 /// * `sender` - The user who owns the token.1923 /// * `token` - Token id that will burned.1924 /// * `self_budget` - The budget that can be spent on burning tokens.1925 /// * `breadth_budget` - The budget that can be spent on burning nested tokens.1926 fn burn_item_recursively(1927 &self,1928 sender: T::CrossAccountId,1929 token: TokenId,1930 self_budget: &dyn Budget,1931 breadth_budget: &dyn Budget,1932 ) -> DispatchResultWithPostInfo;19331934 /// Set collection properties.1935 ///1936 /// * `sender` - Must be either the owner of the collection or its admin.1937 /// * `properties` - Properties to be set.1938 fn set_collection_properties(1939 &self,1940 sender: T::CrossAccountId,1941 properties: Vec<Property>,1942 ) -> DispatchResultWithPostInfo;19431944 /// Delete collection properties.1945 ///1946 /// * `sender` - Must be either the owner of the collection or its admin.1947 /// * `properties` - The properties to be removed.1948 fn delete_collection_properties(1949 &self,1950 sender: &T::CrossAccountId,1951 property_keys: Vec<PropertyKey>,1952 ) -> DispatchResultWithPostInfo;19531954 /// Set token properties.1955 ///1956 /// The appropriate [`PropertyPermission`] for the token property1957 /// must be set with [`Self::set_token_property_permissions`].1958 ///1959 /// * `sender` - Must be either the owner of the token or its admin.1960 /// * `token_id` - The token for which the properties are being set.1961 /// * `properties` - Properties to be set.1962 /// * `budget` - Budget for setting properties.1963 fn set_token_properties(1964 &self,1965 sender: T::CrossAccountId,1966 token_id: TokenId,1967 properties: Vec<Property>,1968 budget: &dyn Budget,1969 ) -> DispatchResultWithPostInfo;19701971 /// Remove token properties.1972 ///1973 /// The appropriate [`PropertyPermission`] for the token property1974 /// must be set with [`Self::set_token_property_permissions`].1975 ///1976 /// * `sender` - Must be either the owner of the token or its admin.1977 /// * `token_id` - The token for which the properties are being remove.1978 /// * `property_keys` - Keys to remove corresponding properties.1979 /// * `budget` - Budget for removing properties.1980 fn delete_token_properties(1981 &self,1982 sender: T::CrossAccountId,1983 token_id: TokenId,1984 property_keys: Vec<PropertyKey>,1985 budget: &dyn Budget,1986 ) -> DispatchResultWithPostInfo;19871988 /// Set token property permissions.1989 ///1990 /// * `sender` - Must be either the owner of the token or its admin.1991 /// * `token_id` - The token for which the properties are being set.1992 /// * `property_permissions` - Property permissions to be set.1993 /// * `budget` - Budget for setting properties.1994 fn set_token_property_permissions(1995 &self,1996 sender: &T::CrossAccountId,1997 property_permissions: Vec<PropertyKeyPermission>,1998 ) -> DispatchResultWithPostInfo;19992000 /// Transfer amount of token pieces.2001 ///2002 /// * `sender` - Donor user.2003 /// * `to` - Recepient user.2004 /// * `token` - The token of which parts are being sent.2005 /// * `amount` - The number of parts of the token that will be transferred.2006 /// * `budget` - The maximum budget that can be spent on the transfer.2007 fn transfer(2008 &self,2009 sender: T::CrossAccountId,2010 to: T::CrossAccountId,2011 token: TokenId,2012 amount: u128,2013 budget: &dyn Budget,2014 ) -> DispatchResultWithPostInfo;20152016 /// Grant access to another account to transfer parts of the token owned by the calling user via [Self::transfer_from].2017 ///2018 /// * `sender` - The user who grants access to the token.2019 /// * `spender` - The user to whom the rights are granted.2020 /// * `token` - The token to which access is granted.2021 /// * `amount` - The amount of pieces that another user can dispose of.2022 fn approve(2023 &self,2024 sender: T::CrossAccountId,2025 spender: T::CrossAccountId,2026 token: TokenId,2027 amount: u128,2028 ) -> DispatchResultWithPostInfo;20292030 /// Grant access to another account to transfer parts of the token owned by the calling user's eth mirror via [Self::transfer_from].2031 ///2032 /// * `sender` - The user who grants access to the token.2033 /// * `from` - Spender's eth mirror.2034 /// * `to` - The user to whom the rights are granted.2035 /// * `token` - The token to which access is granted.2036 /// * `amount` - The amount of pieces that another user can dispose of.2037 fn approve_from(2038 &self,2039 sender: T::CrossAccountId,2040 from: T::CrossAccountId,2041 to: T::CrossAccountId,2042 token: TokenId,2043 amount: u128,2044 ) -> DispatchResultWithPostInfo;20452046 /// Send parts of a token owned by another user.2047 ///2048 /// Before calling this method, you must grant rights to the calling user via [`Self::approve`].2049 ///2050 /// * `sender` - The user who must have access to the token (see [`Self::approve`]).2051 /// * `from` - The user who owns the token.2052 /// * `to` - Recepient user.2053 /// * `token` - The token of which parts are being sent.2054 /// * `amount` - The number of parts of the token that will be transferred.2055 /// * `budget` - The maximum budget that can be spent on the transfer.2056 fn transfer_from(2057 &self,2058 sender: T::CrossAccountId,2059 from: T::CrossAccountId,2060 to: T::CrossAccountId,2061 token: TokenId,2062 amount: u128,2063 budget: &dyn Budget,2064 ) -> DispatchResultWithPostInfo;20652066 /// Burn parts of a token owned by another user.2067 ///2068 /// Before calling this method, you must grant rights to the calling user via [`Self::approve`].2069 ///2070 /// * `sender` - The user who must have access to the token (see [`Self::approve`]).2071 /// * `from` - The user who owns the token.2072 /// * `token` - The token of which parts are being sent.2073 /// * `amount` - The number of parts of the token that will be transferred.2074 /// * `budget` - The maximum budget that can be spent on the burn.2075 fn burn_from(2076 &self,2077 sender: T::CrossAccountId,2078 from: T::CrossAccountId,2079 token: TokenId,2080 amount: u128,2081 budget: &dyn Budget,2082 ) -> DispatchResultWithPostInfo;20832084 /// Check permission to nest token.2085 ///2086 /// * `sender` - The user who initiated the check.2087 /// * `from` - The token that is checked for embedding.2088 /// * `under` - Token under which to check.2089 /// * `budget` - The maximum budget that can be spent on the check.2090 fn check_nesting(2091 &self,2092 sender: T::CrossAccountId,2093 from: (CollectionId, TokenId),2094 under: TokenId,2095 budget: &dyn Budget,2096 ) -> DispatchResult;20972098 /// Nest one token into another.2099 ///2100 /// * `under` - Token holder.2101 /// * `to_nest` - Nested token.2102 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21032104 /// Unnest token.2105 ///2106 /// * `under` - Token holder.2107 /// * `to_nest` - Token to unnest.2108 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21092110 /// Get all user tokens.2111 ///2112 /// * `account` - Account for which you need to get tokens.2113 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21142115 /// Get all the tokens in the collection.2116 fn collection_tokens(&self) -> Vec<TokenId>;21172118 /// Check if the token exists.2119 ///2120 /// * `token` - Id token to check.2121 fn token_exists(&self, token: TokenId) -> bool;21222123 /// Get the id of the last minted token.2124 fn last_token_id(&self) -> TokenId;21252126 /// Get the owner of the token.2127 ///2128 /// * `token` - The token for which you need to find out the owner.2129 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21302131 /// Returns 10 tokens owners in no particular order.2132 ///2133 /// * `token` - The token for which you need to find out the owners.2134 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21352136 /// Get the value of the token property by key.2137 ///2138 /// * `token` - Token with the property to get.2139 /// * `key` - Property name.2140 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21412142 /// Get a set of token properties by key vector.2143 ///2144 /// * `token` - Token with the property to get.2145 /// * `keys` - Vector of property keys. If this parameter is [None](sp_std::result::Result),2146 /// then all properties are returned.2147 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21482149 /// Amount of unique collection tokens2150 fn total_supply(&self) -> u32;21512152 /// Amount of different tokens account has.2153 ///2154 /// * `account` - The account for which need to get the balance.2155 fn account_balance(&self, account: T::CrossAccountId) -> u32;21562157 /// Amount of specific token account have.2158 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21592160 /// Amount of token pieces2161 fn total_pieces(&self, token: TokenId) -> Option<u128>;21622163 /// Get the number of parts of the token that a trusted user can manage.2164 ///2165 /// * `sender` - Trusted user.2166 /// * `spender` - Owner of the token.2167 /// * `token` - The token for which to get the value.2168 fn allowance(2169 &self,2170 sender: T::CrossAccountId,2171 spender: T::CrossAccountId,2172 token: TokenId,2173 ) -> u128;21742175 /// Get extension for RFT collection.2176 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21772178 /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.2179 /// * `owner` - Token owner2180 /// * `operator` - Operator2181 /// * `approve` - Should operator status be granted or revoked?2182 fn set_allowance_for_all(2183 &self,2184 owner: T::CrossAccountId,2185 operator: T::CrossAccountId,2186 approve: bool,2187 ) -> DispatchResultWithPostInfo;21882189 /// Tells whether the given `owner` approves the `operator`.2190 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21912192 /// Repairs a possibly broken item.2193 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2194}21952196/// Extension for RFT collection.2197pub trait RefungibleExtensions<T>2198where2199 T: Config,2200{2201 /// Change the number of parts of the token.2202 ///2203 /// When the value changes down, this function is equivalent to burning parts of the token.2204 ///2205 /// * `sender` - The user calling the repartition operation. Must be the owner of the token.2206 /// * `token` - The token for which you want to change the number of parts.2207 /// * `amount` - The new value of the parts of the token.2208 fn repartition(2209 &self,2210 sender: &T::CrossAccountId,2211 token: TokenId,2212 amount: u128,2213 ) -> DispatchResultWithPostInfo;2214}22152216/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].2217///2218/// Used for [`CommonCollectionOperations`] implementations and flexible enough to do so.2219pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2220 let post_info = PostDispatchInfo {2221 actual_weight: Some(weight),2222 pays_fee: Pays::Yes,2223 };2224 match res {2225 Ok(()) => Ok(post_info),2226 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2227 }2228}22292230impl<T: Config> From<PropertiesError> for Error<T> {2231 fn from(error: PropertiesError) -> Self {2232 match error {2233 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2234 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2235 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2236 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2237 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2238 }2239 }2240}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Common pallet18//!19//! The Common pallet provides functionality for handling collections.20//!21//! ## Overview22//!23//! The Common pallet provides an interface for common collection operations for different collection types24//! (see [CommonCollectionOperations]), as well as a generic dispatcher for these, see [dispatch] module.25//! It also provides this functionality to EVM, see [erc] and [eth] modules.26//!27//! The Common pallet provides functions for:28//!29//! - Setting and approving collection sponsor.30//! - Get\set\delete allow list.31//! - Get\set\delete collection properties.32//! - Get\set\delete collection property permissions.33//! - Get\set\delete token property permissions.34//! - Get\set\delete collection administrators.35//! - Checking access permissions.36//!37//! ### Terminology38//! **Collection sponsor** - For the collection, you can set a sponsor, at whose expense it will39//! be possible to mint tokens.40//!41//! **Allow list** - List of users who have the right to minting tokens.42//!43//! **Collection properties** - Collection properties are simply key-value stores where various44//! metadata can be placed.45//!46//! **Permissions on token properties** - For each property in the token can be set permission47//! to change, see [`PropertyPermission`].48//!49//! **Collection administrator** - For a collection, you can set administrators who have the right50//! to most actions on the collection.5152#![warn(missing_docs)]53#![cfg_attr(not(feature = "std"), no_std)]54extern crate alloc;5556use core::ops::{Deref, DerefMut};57use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};58use sp_std::vec::Vec;59use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};60use evm_coder::ToLog;61use frame_support::{62 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},63 ensure,64 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},65 dispatch::Pays,66 transactional,67};68use pallet_evm::GasWeightMapping;69use up_data_structs::{70 COLLECTION_NUMBER_LIMIT,71 Collection,72 RpcCollection,73 CollectionFlags,74 RpcCollectionFlags,75 CollectionId,76 CreateItemData,77 MAX_TOKEN_PREFIX_LENGTH,78 COLLECTION_ADMINS_LIMIT,79 TokenId,80 TokenChild,81 CollectionStats,82 MAX_TOKEN_OWNERSHIP,83 CollectionMode,84 NFT_SPONSOR_TRANSFER_TIMEOUT,85 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,86 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,87 MAX_SPONSOR_TIMEOUT,88 CUSTOM_DATA_LIMIT,89 CollectionLimits,90 CreateCollectionData,91 SponsorshipState,92 CreateItemExData,93 SponsoringRateLimit,94 budget::Budget,95 PhantomType,96 Property,97 Properties,98 PropertiesPermissionMap,99 PropertyKey,100 PropertyValue,101 PropertyPermission,102 PropertiesError,103 PropertyKeyPermission,104 TokenData,105 TrySetProperty,106 PropertyScope,107 // RMRK108 RmrkCollectionInfo,109 RmrkInstanceInfo,110 RmrkResourceInfo,111 RmrkPropertyInfo,112 RmrkBaseInfo,113 RmrkPartType,114 RmrkBoundedTheme,115 RmrkNftChild,116 CollectionPermissions,117};118use up_pov_estimate_rpc::PovInfo;119120pub use pallet::*;121use sp_core::H160;122use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};123#[cfg(feature = "runtime-benchmarks")]124pub mod benchmarking;125pub mod dispatch;126pub mod erc;127pub mod eth;128pub mod weights;129130/// Weight info.131pub type SelfWeightOf<T> = <T as Config>::WeightInfo;132133/// Collection handle contains information about collection data and id.134/// Also provides functionality to count consumed gas.135///136/// CollectionHandle is used as a generic wrapper for collections of all types.137/// It allows to perform common operations and queries on any collection type,138/// both completely general for all, as well as their respective implementations of [`CommonCollectionOperations`].139#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]140pub struct CollectionHandle<T: Config> {141 /// Collection id142 pub id: CollectionId,143 collection: Collection<T::AccountId>,144 /// Substrate recorder for counting consumed gas145 pub recorder: SubstrateRecorder<T>,146}147148impl<T: Config> WithRecorder<T> for CollectionHandle<T> {149 fn recorder(&self) -> &SubstrateRecorder<T> {150 &self.recorder151 }152 fn into_recorder(self) -> SubstrateRecorder<T> {153 self.recorder154 }155}156157impl<T: Config> CollectionHandle<T> {158 /// Same as [CollectionHandle::new] but with an explicit gas limit.159 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {160 <CollectionById<T>>::get(id).map(|collection| Self {161 id,162 collection,163 recorder: SubstrateRecorder::new(gas_limit),164 })165 }166167 /// Same as [CollectionHandle::new] but with an existed [`SubstrateRecorder`].168 pub fn new_with_recorder(id: CollectionId, recorder: SubstrateRecorder<T>) -> Option<Self> {169 <CollectionById<T>>::get(id).map(|collection| Self {170 id,171 collection,172 recorder,173 })174 }175176 /// Retrives collection data from storage and creates collection handle with default parameters.177 /// If collection not found return `None`178 pub fn new(id: CollectionId) -> Option<Self> {179 Self::new_with_gas_limit(id, u64::MAX)180 }181182 /// Same as [`CollectionHandle::new`] but if collection not found [CollectionNotFound](Error::CollectionNotFound) returned.183 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {184 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)185 }186187 /// Consume gas for reading.188 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {189 self.recorder190 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(191 <T as frame_system::Config>::DbWeight::get()192 .read193 .saturating_mul(reads),194 )))195 }196197 /// Consume gas for writing.198 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {199 self.recorder200 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(201 <T as frame_system::Config>::DbWeight::get()202 .write203 .saturating_mul(writes),204 )))205 }206207 /// Consume gas for reading and writing.208 pub fn consume_store_reads_and_writes(209 &self,210 reads: u64,211 writes: u64,212 ) -> evm_coder::execution::Result<()> {213 let weight = <T as frame_system::Config>::DbWeight::get();214 let reads = weight.read.saturating_mul(reads);215 let writes = weight.read.saturating_mul(writes);216 self.recorder217 .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time(218 reads.saturating_add(writes),219 )))220 }221222 /// Save collection to storage.223 pub fn save(&self) -> DispatchResult {224 <CollectionById<T>>::insert(self.id, &self.collection);225 Ok(())226 }227228 /// Set collection sponsor.229 ///230 /// Unique collections allows sponsoring for certain actions.231 /// This method allows you to set the sponsor of the collection.232 /// In order for sponsorship to become active, it must be confirmed through [`Self::confirm_sponsorship`].233 pub fn set_sponsor(234 &mut self,235 sender: &T::CrossAccountId,236 sponsor: T::AccountId,237 ) -> DispatchResult {238 self.check_is_internal()?;239 self.check_is_owner_or_admin(sender)?;240241 self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor.clone());242243 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor));244 <PalletEvm<T>>::deposit_log(245 erc::CollectionHelpersEvents::CollectionChanged {246 collection_id: eth::collection_id_to_address(self.id),247 }248 .to_log(T::ContractAddress::get()),249 );250251 self.save()252 }253254 /// Force set `sponsor`.255 ///256 /// Differs from [`set_sponsor`][`Self::set_sponsor`] in that confirmation257 /// from the `sponsor` is not required.258 ///259 /// # Arguments260 ///261 /// * `sender`: Caller's account.262 /// * `sponsor`: ID of the account of the sponsor-to-be.263 pub fn force_set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {264 self.check_is_internal()?;265266 self.collection.sponsorship = SponsorshipState::Confirmed(sponsor.clone());267268 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(self.id, sponsor.clone()));269 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sponsor));270 <PalletEvm<T>>::deposit_log(271 erc::CollectionHelpersEvents::CollectionChanged {272 collection_id: eth::collection_id_to_address(self.id),273 }274 .to_log(T::ContractAddress::get()),275 );276277 self.save()278 }279280 /// Confirm sponsorship281 ///282 /// In order for the sponsorship to become active, the user set as the sponsor must confirm their participation.283 /// Before confirming sponsorship, the user must be specified as the sponsor of the collection via [`Self::set_sponsor`].284 pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> DispatchResult {285 self.check_is_internal()?;286 ensure!(287 self.collection.sponsorship.pending_sponsor() == Some(sender),288 Error::<T>::ConfirmSponsorshipFail289 );290291 self.collection.sponsorship = SponsorshipState::Confirmed(sender.clone());292293 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(self.id, sender.clone()));294 <PalletEvm<T>>::deposit_log(295 erc::CollectionHelpersEvents::CollectionChanged {296 collection_id: eth::collection_id_to_address(self.id),297 }298 .to_log(T::ContractAddress::get()),299 );300301 self.save()302 }303304 /// Remove collection sponsor.305 pub fn remove_sponsor(&mut self, sender: &T::CrossAccountId) -> DispatchResult {306 self.check_is_internal()?;307 self.check_is_owner_or_admin(sender)?;308309 self.collection.sponsorship = SponsorshipState::Disabled;310311 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));312 <PalletEvm<T>>::deposit_log(313 erc::CollectionHelpersEvents::CollectionChanged {314 collection_id: eth::collection_id_to_address(self.id),315 }316 .to_log(T::ContractAddress::get()),317 );318 self.save()319 }320321 /// Force remove `sponsor`.322 ///323 /// Differs from `remove_sponsor` in that324 /// it doesn't require consent from the `owner` of the collection.325 pub fn force_remove_sponsor(&mut self) -> DispatchResult {326 self.check_is_internal()?;327328 self.collection.sponsorship = SponsorshipState::Disabled;329330 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(self.id));331 <PalletEvm<T>>::deposit_log(332 erc::CollectionHelpersEvents::CollectionChanged {333 collection_id: eth::collection_id_to_address(self.id),334 }335 .to_log(T::ContractAddress::get()),336 );337 self.save()338 }339340 /// Checks that the collection was created with, and must be operated upon through **Unique API**.341 /// Now check only the `external` flag and if it's **true**, then return [`Error::CollectionIsExternal`] error.342 pub fn check_is_internal(&self) -> DispatchResult {343 if self.flags.external {344 return Err(<Error<T>>::CollectionIsExternal)?;345 }346347 Ok(())348 }349350 /// Checks that the collection was created with, and must be operated upon through an **assimilated API**.351 /// Now check only the `external` flag and if it's **false**, then return [`Error::CollectionIsInternal`] error.352 pub fn check_is_external(&self) -> DispatchResult {353 if !self.flags.external {354 return Err(<Error<T>>::CollectionIsInternal)?;355 }356357 Ok(())358 }359}360361impl<T: Config> Deref for CollectionHandle<T> {362 type Target = Collection<T::AccountId>;363364 fn deref(&self) -> &Self::Target {365 &self.collection366 }367}368369impl<T: Config> DerefMut for CollectionHandle<T> {370 fn deref_mut(&mut self) -> &mut Self::Target {371 &mut self.collection372 }373}374375impl<T: Config> CollectionHandle<T> {376 /// Checks if the `user` is the owner of the collection.377 pub fn check_is_owner(&self, user: &T::CrossAccountId) -> DispatchResult {378 ensure!(*user.as_sub() == self.owner, <Error<T>>::NoPermission);379 Ok(())380 }381382 /// Returns **true** if the `user` is the owner or administrator of the collection.383 pub fn is_owner_or_admin(&self, user: &T::CrossAccountId) -> bool {384 *user.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, user))385 }386387 /// Checks if the `user` is the owner or administrator of the collection.388 pub fn check_is_owner_or_admin(&self, user: &T::CrossAccountId) -> DispatchResult {389 ensure!(self.is_owner_or_admin(user), <Error<T>>::NoPermission);390 Ok(())391 }392393 /// Returns **true** if394 /// * the `user`is a collection owner or admin395 /// * the collection limits allow the owner/admins to transfer/burn any collection token396 pub fn ignores_token_restrictions(&self, user: &T::CrossAccountId) -> bool {397 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)398 }399400 /// Return **true** if `user` does not have enough token parts, and he can ignore such restrictions.401 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {402 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)403 }404405 /// Checks if the user is in the allow list. If not [Error::AddressNotInAllowlist] returns.406 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {407 ensure!(408 <Allowlist<T>>::get((self.id, user)),409 <Error<T>>::AddressNotInAllowlist410 );411 Ok(())412 }413414 /// Changes collection owner to another account415 /// #### Store read/writes416 /// 1 writes417 pub fn change_owner(418 &mut self,419 caller: T::CrossAccountId,420 new_owner: T::CrossAccountId,421 ) -> DispatchResult {422 self.check_is_internal()?;423 self.check_is_owner(&caller)?;424 self.collection.owner = new_owner.as_sub().clone();425426 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnerChanged(427 self.id,428 new_owner.as_sub().clone(),429 ));430 <PalletEvm<T>>::deposit_log(431 erc::CollectionHelpersEvents::CollectionChanged {432 collection_id: eth::collection_id_to_address(self.id),433 }434 .to_log(T::ContractAddress::get()),435 );436437 self.save()438 }439}440441#[frame_support::pallet]442pub mod pallet {443 use super::*;444 use dispatch::CollectionDispatch;445 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};446 use frame_system::pallet_prelude::*;447 use frame_support::traits::Currency;448 use up_data_structs::{TokenId, mapping::TokenAddressMapping};449 use scale_info::TypeInfo;450 use weights::WeightInfo;451452 #[pallet::config]453 pub trait Config:454 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config + TypeInfo455 {456 /// Weight information for functions of this pallet.457 type WeightInfo: WeightInfo;458459 /// Events compatible with [`frame_system::Config::Event`].460 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;461462 /// Handler of accounts and payment.463 type Currency: Currency<Self::AccountId>;464465 /// Set price to create a collection.466 #[pallet::constant]467 type CollectionCreationPrice: Get<468 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,469 >;470471 /// Dispatcher of operations on collections.472 type CollectionDispatch: CollectionDispatch<Self>;473474 /// Account which holds the chain's treasury.475 type TreasuryAccountId: Get<Self::AccountId>;476477 /// Address under which the CollectionHelper contract would be available.478 #[pallet::constant]479 type ContractAddress: Get<H160>;480481 /// Mapper for token addresses to Ethereum addresses.482 type EvmTokenAddressMapping: TokenAddressMapping<H160>;483484 /// Mapper for token addresses to [`CrossAccountId`].485 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;486 }487488 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);489490 #[pallet::pallet]491 #[pallet::storage_version(STORAGE_VERSION)]492 #[pallet::generate_store(pub(super) trait Store)]493 pub struct Pallet<T>(_);494495 #[pallet::extra_constants]496 impl<T: Config> Pallet<T> {497 /// Maximum admins per collection.498 pub fn collection_admins_limit() -> u32 {499 COLLECTION_ADMINS_LIMIT500 }501 }502503 impl<T: Config> Pallet<T> {504 /// Helper function that handles deposit events505 pub fn deposit_event(event: Event<T>) {506 let event = <T as Config>::RuntimeEvent::from(event);507 let event = event.into();508 <frame_system::Pallet<T>>::deposit_event(event)509 }510 }511512 #[pallet::event]513 pub enum Event<T: Config> {514 /// New collection was created515 CollectionCreated(516 /// Globally unique identifier of newly created collection.517 CollectionId,518 /// [`CollectionMode`] converted into _u8_.519 u8,520 /// Collection owner.521 T::AccountId,522 ),523524 /// New collection was destroyed525 CollectionDestroyed(526 /// Globally unique identifier of collection.527 CollectionId,528 ),529530 /// New item was created.531 ItemCreated(532 /// Id of the collection where item was created.533 CollectionId,534 /// Id of an item. Unique within the collection.535 TokenId,536 /// Owner of newly created item537 T::CrossAccountId,538 /// Always 1 for NFT539 u128,540 ),541542 /// Collection item was burned.543 ItemDestroyed(544 /// Id of the collection where item was destroyed.545 CollectionId,546 /// Identifier of burned NFT.547 TokenId,548 /// Which user has destroyed its tokens.549 T::CrossAccountId,550 /// Amount of token pieces destroed. Always 1 for NFT.551 u128,552 ),553554 /// Item was transferred555 Transfer(556 /// Id of collection to which item is belong.557 CollectionId,558 /// Id of an item.559 TokenId,560 /// Original owner of item.561 T::CrossAccountId,562 /// New owner of item.563 T::CrossAccountId,564 /// Amount of token pieces transfered. Always 1 for NFT.565 u128,566 ),567568 /// Amount pieces of token owned by `sender` was approved for `spender`.569 Approved(570 /// Id of collection to which item is belong.571 CollectionId,572 /// Id of an item.573 TokenId,574 /// Original owner of item.575 T::CrossAccountId,576 /// Id for which the approval was granted.577 T::CrossAccountId,578 /// Amount of token pieces transfered. Always 1 for NFT.579 u128,580 ),581582 /// A `sender` approves operations on all owned tokens for `spender`.583 ApprovedForAll(584 /// Id of collection to which item is belong.585 CollectionId,586 /// Owner of a wallet.587 T::CrossAccountId,588 /// Id for which operator status was granted or rewoked.589 T::CrossAccountId,590 /// Is operator status granted or revoked?591 bool,592 ),593594 /// The colletion property has been added or edited.595 CollectionPropertySet(596 /// Id of collection to which property has been set.597 CollectionId,598 /// The property that was set.599 PropertyKey,600 ),601602 /// The property has been deleted.603 CollectionPropertyDeleted(604 /// Id of collection to which property has been deleted.605 CollectionId,606 /// The property that was deleted.607 PropertyKey,608 ),609610 /// The token property has been added or edited.611 TokenPropertySet(612 /// Identifier of the collection whose token has the property set.613 CollectionId,614 /// The token for which the property was set.615 TokenId,616 /// The property that was set.617 PropertyKey,618 ),619620 /// The token property has been deleted.621 TokenPropertyDeleted(622 /// Identifier of the collection whose token has the property deleted.623 CollectionId,624 /// The token for which the property was deleted.625 TokenId,626 /// The property that was deleted.627 PropertyKey,628 ),629630 /// The token property permission of a collection has been set.631 PropertyPermissionSet(632 /// ID of collection to which property permission has been set.633 CollectionId,634 /// The property permission that was set.635 PropertyKey,636 ),637638 /// Address was added to the allow list.639 AllowListAddressAdded(640 /// ID of the affected collection.641 CollectionId,642 /// Address of the added account.643 T::CrossAccountId,644 ),645646 /// Address was removed from the allow list.647 AllowListAddressRemoved(648 /// ID of the affected collection.649 CollectionId,650 /// Address of the removed account.651 T::CrossAccountId,652 ),653654 /// Collection admin was added.655 CollectionAdminAdded(656 /// ID of the affected collection.657 CollectionId,658 /// Admin address.659 T::CrossAccountId,660 ),661662 /// Collection admin was removed.663 CollectionAdminRemoved(664 /// ID of the affected collection.665 CollectionId,666 /// Removed admin address.667 T::CrossAccountId,668 ),669670 /// Collection limits were set.671 CollectionLimitSet(672 /// ID of the affected collection.673 CollectionId,674 ),675676 /// Collection owned was changed.677 CollectionOwnerChanged(678 /// ID of the affected collection.679 CollectionId,680 /// New owner address.681 T::AccountId,682 ),683684 /// Collection permissions were set.685 CollectionPermissionSet(686 /// ID of the affected collection.687 CollectionId,688 ),689690 /// Collection sponsor was set.691 CollectionSponsorSet(692 /// ID of the affected collection.693 CollectionId,694 /// New sponsor address.695 T::AccountId,696 ),697698 /// New sponsor was confirm.699 SponsorshipConfirmed(700 /// ID of the affected collection.701 CollectionId,702 /// New sponsor address.703 T::AccountId,704 ),705706 /// Collection sponsor was removed.707 CollectionSponsorRemoved(708 /// ID of the affected collection.709 CollectionId,710 ),711 }712713 #[pallet::error]714 pub enum Error<T> {715 /// This collection does not exist.716 CollectionNotFound,717 /// Sender parameter and item owner must be equal.718 MustBeTokenOwner,719 /// No permission to perform action720 NoPermission,721 /// Destroying only empty collections is allowed722 CantDestroyNotEmptyCollection,723 /// Collection is not in mint mode.724 PublicMintingNotAllowed,725 /// Address is not in allow list.726 AddressNotInAllowlist,727728 /// Collection name can not be longer than 63 char.729 CollectionNameLimitExceeded,730 /// Collection description can not be longer than 255 char.731 CollectionDescriptionLimitExceeded,732 /// Token prefix can not be longer than 15 char.733 CollectionTokenPrefixLimitExceeded,734 /// Total collections bound exceeded.735 TotalCollectionsLimitExceeded,736 /// Exceeded max admin count737 CollectionAdminCountExceeded,738 /// Collection limit bounds per collection exceeded739 CollectionLimitBoundsExceeded,740 /// Tried to enable permissions which are only permitted to be disabled741 OwnerPermissionsCantBeReverted,742 /// Collection settings not allowing items transferring743 TransferNotAllowed,744 /// Account token limit exceeded per collection745 AccountTokenLimitExceeded,746 /// Collection token limit exceeded747 CollectionTokenLimitExceeded,748 /// Metadata flag frozen749 MetadataFlagFrozen,750751 /// Item does not exist752 TokenNotFound,753 /// Item is balance not enough754 TokenValueTooLow,755 /// Requested value is more than the approved756 ApprovedValueTooLow,757 /// Tried to approve more than owned758 CantApproveMoreThanOwned,759 /// Only spending from eth mirror could be approved760 AddressIsNotEthMirror,761762 /// Can't transfer tokens to ethereum zero address763 AddressIsZero,764765 /// The operation is not supported766 UnsupportedOperation,767768 /// Insufficient funds to perform an action769 NotSufficientFounds,770771 /// User does not satisfy the nesting rule772 UserIsNotAllowedToNest,773 /// Only tokens from specific collections may nest tokens under this one774 SourceCollectionIsNotAllowedToNest,775776 /// Tried to store more data than allowed in collection field777 CollectionFieldSizeExceeded,778779 /// Tried to store more property data than allowed780 NoSpaceForProperty,781782 /// Tried to store more property keys than allowed783 PropertyLimitReached,784785 /// Property key is too long786 PropertyKeyIsTooLong,787788 /// Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed789 InvalidCharacterInPropertyKey,790791 /// Empty property keys are forbidden792 EmptyPropertyKey,793794 /// Tried to access an external collection with an internal API795 CollectionIsExternal,796797 /// Tried to access an internal collection with an external API798 CollectionIsInternal,799800 /// This address is not set as sponsor, use setCollectionSponsor first.801 ConfirmSponsorshipFail,802803 /// The user is not an administrator.804 UserIsNotCollectionAdmin,805 }806807 /// Storage of the count of created collections. Essentially contains the last collection ID.808 #[pallet::storage]809 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;810811 /// Storage of the count of deleted collections.812 #[pallet::storage]813 pub type DestroyedCollectionCount<T> =814 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;815816 /// Storage of collection info.817 #[pallet::storage]818 pub type CollectionById<T> = StorageMap<819 Hasher = Blake2_128Concat,820 Key = CollectionId,821 Value = Collection<<T as frame_system::Config>::AccountId>,822 QueryKind = OptionQuery,823 >;824825 /// Storage of collection properties.826 #[pallet::storage]827 #[pallet::getter(fn collection_properties)]828 pub type CollectionProperties<T> = StorageMap<829 Hasher = Blake2_128Concat,830 Key = CollectionId,831 Value = Properties,832 QueryKind = ValueQuery,833 OnEmpty = up_data_structs::CollectionProperties,834 >;835836 /// Storage of token property permissions of a collection.837 #[pallet::storage]838 #[pallet::getter(fn property_permissions)]839 pub type CollectionPropertyPermissions<T> = StorageMap<840 Hasher = Blake2_128Concat,841 Key = CollectionId,842 Value = PropertiesPermissionMap,843 QueryKind = ValueQuery,844 >;845846 /// Storage of the amount of collection admins.847 #[pallet::storage]848 pub type AdminAmount<T> = StorageMap<849 Hasher = Blake2_128Concat,850 Key = CollectionId,851 Value = u32,852 QueryKind = ValueQuery,853 >;854855 /// List of collection admins.856 #[pallet::storage]857 pub type IsAdmin<T: Config> = StorageNMap<858 Key = (859 Key<Blake2_128Concat, CollectionId>,860 Key<Blake2_128Concat, T::CrossAccountId>,861 ),862 Value = bool,863 QueryKind = ValueQuery,864 >;865866 /// Allowlisted collection users.867 #[pallet::storage]868 pub type Allowlist<T: Config> = StorageNMap<869 Key = (870 Key<Blake2_128Concat, CollectionId>,871 Key<Blake2_128Concat, T::CrossAccountId>,872 ),873 Value = bool,874 QueryKind = ValueQuery,875 >;876877 /// Not used by code, exists only to provide some types to metadata.878 #[pallet::storage]879 pub type DummyStorageValue<T: Config> = StorageValue<880 Value = (881 CollectionStats,882 CollectionId,883 TokenId,884 TokenChild,885 PhantomType<(886 TokenData<T::CrossAccountId>,887 RpcCollection<T::AccountId>,888 // RMRK889 RmrkCollectionInfo<T::AccountId>,890 RmrkInstanceInfo<T::AccountId>,891 RmrkResourceInfo,892 RmrkPropertyInfo,893 RmrkBaseInfo<T::AccountId>,894 RmrkPartType,895 RmrkBoundedTheme,896 RmrkNftChild,897 // PoV Estimate Info898 PovInfo,899 )>,900 ),901 QueryKind = OptionQuery,902 >;903904 #[pallet::hooks]905 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {906 fn on_runtime_upgrade() -> Weight {907 StorageVersion::new(1).put::<Pallet<T>>();908909 Weight::zero()910 }911 }912}913914impl<T: Config> Pallet<T> {915 /// Enshure that receiver address is correct.916 ///917 /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens.918 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {919 ensure!(920 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,921 <Error<T>>::AddressIsZero922 );923 Ok(())924 }925926 /// Get a vector of collection admins.927 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {928 <IsAdmin<T>>::iter_prefix((collection,))929 .map(|(a, _)| a)930 .collect()931 }932933 /// Get a vector of users allowed to mint tokens.934 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {935 <Allowlist<T>>::iter_prefix((collection,))936 .map(|(a, _)| a)937 .collect()938 }939940 /// Is `user` allowed to mint token in `collection`.941 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {942 <Allowlist<T>>::get((collection, user))943 }944945 /// Get statistics of collections.946 pub fn collection_stats() -> CollectionStats {947 let created = <CreatedCollectionCount<T>>::get();948 let destroyed = <DestroyedCollectionCount<T>>::get();949 CollectionStats {950 created: created.0,951 destroyed: destroyed.0,952 alive: created.0 - destroyed.0,953 }954 }955956 /// Get the effective limits for the collection.957 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {958 let collection = <CollectionById<T>>::get(collection)?;959 let limits = collection.limits;960 let effective_limits = CollectionLimits {961 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),962 sponsored_data_size: Some(limits.sponsored_data_size()),963 sponsored_data_rate_limit: Some(964 limits965 .sponsored_data_rate_limit966 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),967 ),968 token_limit: Some(limits.token_limit()),969 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(970 match collection.mode {971 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,972 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,973 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,974 },975 )),976 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),977 owner_can_transfer: Some(limits.owner_can_transfer()),978 owner_can_destroy: Some(limits.owner_can_destroy()),979 transfers_enabled: Some(limits.transfers_enabled()),980 };981982 Some(effective_limits)983 }984985 /// Returns information about the `collection` adapted for rpc.986 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {987 let Collection {988 name,989 description,990 owner,991 mode,992 token_prefix,993 sponsorship,994 limits,995 permissions,996 flags,997 } = <CollectionById<T>>::get(collection)?;998999 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)1000 .into_iter()1001 .map(|(key, permission)| PropertyKeyPermission { key, permission })1002 .collect();10031004 let properties = <CollectionProperties<T>>::get(collection)1005 .into_iter()1006 .map(|(key, value)| Property { key, value })1007 .collect();10081009 let permissions = CollectionPermissions {1010 access: Some(permissions.access()),1011 mint_mode: Some(permissions.mint_mode()),1012 nesting: Some(permissions.nesting().clone()),1013 };10141015 Some(RpcCollection {1016 name: name.into_inner(),1017 description: description.into_inner(),1018 owner,1019 mode,1020 token_prefix: token_prefix.into_inner(),1021 sponsorship,1022 limits,1023 permissions,1024 token_property_permissions,1025 properties,1026 read_only: flags.external,10271028 flags: RpcCollectionFlags {1029 foreign: flags.foreign,1030 erc721metadata: flags.erc721metadata,1031 },1032 })1033 }1034}10351036macro_rules! limit_default {1037 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1038 $(1039 if let Some($new) = $new.$field {1040 let $old = $old.$field($($arg)?);1041 let _ = $new;1042 let _ = $old;1043 $check1044 } else {1045 $new.$field = $old.$field1046 }1047 )*1048 }};1049}1050macro_rules! limit_default_clone {1051 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1052 $(1053 if let Some($new) = $new.$field.clone() {1054 let $old = $old.$field($($arg)?);1055 let _ = $new;1056 let _ = $old;1057 $check1058 } else {1059 $new.$field = $old.$field.clone()1060 }1061 )*1062 }};1063}10641065impl<T: Config> Pallet<T> {1066 /// Create new collection.1067 ///1068 /// * `owner` - The owner of the collection.1069 /// * `data` - Description of the created collection.1070 /// * `flags` - Extra flags to store.1071 pub fn init_collection(1072 owner: T::CrossAccountId,1073 payer: T::CrossAccountId,1074 data: CreateCollectionData<T::AccountId>,1075 flags: CollectionFlags,1076 ) -> Result<CollectionId, DispatchError> {1077 {1078 ensure!(1079 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,1080 Error::<T>::CollectionTokenPrefixLimitExceeded1081 );1082 }10831084 let created_count = <CreatedCollectionCount<T>>::get()1085 .01086 .checked_add(1)1087 .ok_or(ArithmeticError::Overflow)?;1088 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;1089 let id = CollectionId(created_count);10901091 // bound Total number of collections1092 ensure!(1093 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,1094 <Error<T>>::TotalCollectionsLimitExceeded1095 );10961097 // =========10981099 let collection = Collection {1100 owner: owner.as_sub().clone(),1101 name: data.name,1102 mode: data.mode.clone(),1103 description: data.description,1104 token_prefix: data.token_prefix,1105 sponsorship: data1106 .pending_sponsor1107 .map(SponsorshipState::Unconfirmed)1108 .unwrap_or_default(),1109 limits: data1110 .limits1111 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))1112 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,1113 permissions: data1114 .permissions1115 .map(|permissions| {1116 Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)1117 })1118 .unwrap_or_else(|| Ok(CollectionPermissions::default()))?,1119 flags,1120 };11211122 let mut collection_properties = up_data_structs::CollectionProperties::get();1123 collection_properties1124 .try_set_from_iter(data.properties.into_iter())1125 .map_err(<Error<T>>::from)?;11261127 CollectionProperties::<T>::insert(id, collection_properties);11281129 let mut token_props_permissions = PropertiesPermissionMap::new();1130 token_props_permissions1131 .try_set_from_iter(data.token_property_permissions.into_iter())1132 .map_err(<Error<T>>::from)?;11331134 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);11351136 // Take a (non-refundable) deposit of collection creation1137 {1138 let mut imbalance =1139 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();1140 imbalance.subsume(1141 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(1142 &T::TreasuryAccountId::get(),1143 T::CollectionCreationPrice::get(),1144 ),1145 );1146 <T as Config>::Currency::settle(1147 payer.as_sub(),1148 imbalance,1149 WithdrawReasons::TRANSFER,1150 ExistenceRequirement::KeepAlive,1151 )1152 .map_err(|_| Error::<T>::NotSufficientFounds)?;1153 }11541155 <CreatedCollectionCount<T>>::put(created_count);1156 <Pallet<T>>::deposit_event(Event::CollectionCreated(1157 id,1158 data.mode.id(),1159 owner.as_sub().clone(),1160 ));1161 <PalletEvm<T>>::deposit_log(1162 erc::CollectionHelpersEvents::CollectionCreated {1163 owner: *owner.as_eth(),1164 collection_id: eth::collection_id_to_address(id),1165 }1166 .to_log(T::ContractAddress::get()),1167 );1168 <CollectionById<T>>::insert(id, collection);1169 Ok(id)1170 }11711172 /// Destroy collection.1173 ///1174 /// * `collection` - Collection handler.1175 /// * `sender` - The owner or administrator of the collection.1176 pub fn destroy_collection(1177 collection: CollectionHandle<T>,1178 sender: &T::CrossAccountId,1179 ) -> DispatchResult {1180 ensure!(1181 collection.limits.owner_can_destroy(),1182 <Error<T>>::NoPermission,1183 );1184 collection.check_is_owner(sender)?;11851186 let destroyed_collections = <DestroyedCollectionCount<T>>::get()1187 .01188 .checked_add(1)1189 .ok_or(ArithmeticError::Overflow)?;11901191 // =========11921193 <DestroyedCollectionCount<T>>::put(destroyed_collections);1194 <CollectionById<T>>::remove(collection.id);1195 <AdminAmount<T>>::remove(collection.id);1196 let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);1197 let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);1198 <CollectionProperties<T>>::remove(collection.id);11991200 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));12011202 <PalletEvm<T>>::deposit_log(1203 erc::CollectionHelpersEvents::CollectionDestroyed {1204 collection_id: eth::collection_id_to_address(collection.id),1205 }1206 .to_log(T::ContractAddress::get()),1207 );1208 Ok(())1209 }12101211 /// This function sets or removes a collection properties according to1212 /// `properties_updates` contents:1213 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`1214 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.1215 ///1216 /// This function fires an event for each property change.1217 /// In case of an error, all the changes (including the events) will be reverted1218 /// since the function is transactional.1219 #[transactional]1220 fn modify_collection_properties(1221 collection: &CollectionHandle<T>,1222 sender: &T::CrossAccountId,1223 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1224 ) -> DispatchResult {1225 collection.check_is_owner_or_admin(sender)?;12261227 let mut stored_properties = <CollectionProperties<T>>::get(collection.id);12281229 for (key, value) in properties_updates {1230 match value {1231 Some(value) => {1232 stored_properties1233 .try_set(key.clone(), value)1234 .map_err(<Error<T>>::from)?;12351236 Self::deposit_event(Event::CollectionPropertySet(collection.id, key));1237 <PalletEvm<T>>::deposit_log(1238 erc::CollectionHelpersEvents::CollectionChanged {1239 collection_id: eth::collection_id_to_address(collection.id),1240 }1241 .to_log(T::ContractAddress::get()),1242 );1243 }1244 None => {1245 stored_properties.remove(&key).map_err(<Error<T>>::from)?;12461247 Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, key));1248 <PalletEvm<T>>::deposit_log(1249 erc::CollectionHelpersEvents::CollectionChanged {1250 collection_id: eth::collection_id_to_address(collection.id),1251 }1252 .to_log(T::ContractAddress::get()),1253 );1254 }1255 }1256 }12571258 <CollectionProperties<T>>::set(collection.id, stored_properties);12591260 Ok(())1261 }12621263 /// Set collection property.1264 ///1265 /// * `collection` - Collection handler.1266 /// * `sender` - The owner or administrator of the collection.1267 /// * `property` - The property to set.1268 pub fn set_collection_property(1269 collection: &CollectionHandle<T>,1270 sender: &T::CrossAccountId,1271 property: Property,1272 ) -> DispatchResult {1273 Self::set_collection_properties(collection, sender, [property].into_iter())1274 }12751276 /// Set a scoped collection property, where the scope is a special prefix1277 /// prohibiting a user access to change the property directly.1278 ///1279 /// * `collection_id` - ID of the collection for which the property is being set.1280 /// * `scope` - Property scope.1281 /// * `property` - The property to set.1282 pub fn set_scoped_collection_property(1283 collection_id: CollectionId,1284 scope: PropertyScope,1285 property: Property,1286 ) -> DispatchResult {1287 CollectionProperties::<T>::try_mutate(collection_id, |properties| {1288 properties.try_scoped_set(scope, property.key, property.value)1289 })1290 .map_err(<Error<T>>::from)?;12911292 Ok(())1293 }12941295 /// Set scoped collection properties, where the scope is a special prefix1296 /// prohibiting a user access to change the properties directly.1297 ///1298 /// * `collection_id` - ID of the collection for which the properties is being set.1299 /// * `scope` - Property scope.1300 /// * `properties` - The properties to set.1301 pub fn set_scoped_collection_properties(1302 collection_id: CollectionId,1303 scope: PropertyScope,1304 properties: impl Iterator<Item = Property>,1305 ) -> DispatchResult {1306 CollectionProperties::<T>::try_mutate(collection_id, |stored_properties| {1307 stored_properties.try_scoped_set_from_iter(scope, properties)1308 })1309 .map_err(<Error<T>>::from)?;13101311 Ok(())1312 }13131314 /// Set collection properties.1315 ///1316 /// * `collection` - Collection handler.1317 /// * `sender` - The owner or administrator of the collection.1318 /// * `properties` - The properties to set.1319 pub fn set_collection_properties(1320 collection: &CollectionHandle<T>,1321 sender: &T::CrossAccountId,1322 properties: impl Iterator<Item = Property>,1323 ) -> DispatchResult {1324 Self::modify_collection_properties(1325 collection,1326 sender,1327 properties.map(|property| (property.key, Some(property.value))),1328 )1329 }13301331 /// Delete collection property.1332 ///1333 /// * `collection` - Collection handler.1334 /// * `sender` - The owner or administrator of the collection.1335 /// * `property` - The property to delete.1336 pub fn delete_collection_property(1337 collection: &CollectionHandle<T>,1338 sender: &T::CrossAccountId,1339 property_key: PropertyKey,1340 ) -> DispatchResult {1341 Self::delete_collection_properties(collection, sender, [property_key].into_iter())1342 }13431344 /// Delete collection properties.1345 ///1346 /// * `collection` - Collection handler.1347 /// * `sender` - The owner or administrator of the collection.1348 /// * `properties` - The properties to delete.1349 pub fn delete_collection_properties(1350 collection: &CollectionHandle<T>,1351 sender: &T::CrossAccountId,1352 property_keys: impl Iterator<Item = PropertyKey>,1353 ) -> DispatchResult {1354 Self::modify_collection_properties(collection, sender, property_keys.map(|key| (key, None)))1355 }13561357 /// Set collection propetry permission without any checks.1358 ///1359 /// Used for migrations.1360 ///1361 /// * `collection` - Collection handler.1362 /// * `property_permissions` - Property permissions.1363 pub fn set_property_permission_unchecked(1364 collection: CollectionId,1365 property_permission: PropertyKeyPermission,1366 ) -> DispatchResult {1367 <CollectionPropertyPermissions<T>>::try_mutate(collection, |permissions| {1368 permissions.try_set(property_permission.key, property_permission.permission)1369 })1370 .map_err(<Error<T>>::from)?;1371 Ok(())1372 }13731374 /// Set collection property permission.1375 ///1376 /// * `collection` - Collection handler.1377 /// * `sender` - The owner or administrator of the collection.1378 /// * `property_permission` - Property permission.1379 pub fn set_property_permission(1380 collection: &CollectionHandle<T>,1381 sender: &T::CrossAccountId,1382 property_permission: PropertyKeyPermission,1383 ) -> DispatchResult {1384 Self::set_scoped_property_permission(1385 collection,1386 sender,1387 PropertyScope::None,1388 property_permission,1389 )1390 }13911392 /// Set collection property permission with scope.1393 ///1394 /// * `collection` - Collection handler.1395 /// * `sender` - The owner or administrator of the collection.1396 /// * `scope` - Property scope.1397 /// * `property_permission` - Property permission.1398 pub fn set_scoped_property_permission(1399 collection: &CollectionHandle<T>,1400 sender: &T::CrossAccountId,1401 scope: PropertyScope,1402 property_permission: PropertyKeyPermission,1403 ) -> DispatchResult {1404 collection.check_is_owner_or_admin(sender)?;14051406 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);1407 let current_permission = all_permissions.get(&property_permission.key);1408 if matches![1409 current_permission,1410 Some(PropertyPermission { mutable: false, .. })1411 ] {1412 return Err(<Error<T>>::NoPermission.into());1413 }14141415 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {1416 let property_permission = property_permission.clone();1417 permissions.try_scoped_set(1418 scope,1419 property_permission.key,1420 property_permission.permission,1421 )1422 })1423 .map_err(<Error<T>>::from)?;14241425 Self::deposit_event(Event::PropertyPermissionSet(1426 collection.id,1427 property_permission.key,1428 ));1429 <PalletEvm<T>>::deposit_log(1430 erc::CollectionHelpersEvents::CollectionChanged {1431 collection_id: eth::collection_id_to_address(collection.id),1432 }1433 .to_log(T::ContractAddress::get()),1434 );14351436 Ok(())1437 }14381439 /// Set token property permission.1440 ///1441 /// * `collection` - Collection handler.1442 /// * `sender` - The owner or administrator of the collection.1443 /// * `property_permissions` - Property permissions.1444 #[transactional]1445 pub fn set_token_property_permissions(1446 collection: &CollectionHandle<T>,1447 sender: &T::CrossAccountId,1448 property_permissions: Vec<PropertyKeyPermission>,1449 ) -> DispatchResult {1450 Self::set_scoped_token_property_permissions(1451 collection,1452 sender,1453 PropertyScope::None,1454 property_permissions,1455 )1456 }14571458 /// Set token property permission with scope.1459 ///1460 /// * `collection` - Collection handler.1461 /// * `sender` - The owner or administrator of the collection.1462 /// * `scope` - Property scope.1463 /// * `property_permissions` - Property permissions.1464 #[transactional]1465 pub fn set_scoped_token_property_permissions(1466 collection: &CollectionHandle<T>,1467 sender: &T::CrossAccountId,1468 scope: PropertyScope,1469 property_permissions: Vec<PropertyKeyPermission>,1470 ) -> DispatchResult {1471 for prop_pemission in property_permissions {1472 Self::set_scoped_property_permission(collection, sender, scope, prop_pemission)?;1473 }14741475 Ok(())1476 }14771478 /// Get collection property.1479 pub fn get_collection_property(1480 collection_id: CollectionId,1481 key: &PropertyKey,1482 ) -> Option<PropertyValue> {1483 Self::collection_properties(collection_id).get(key).cloned()1484 }14851486 /// Convert byte vector to property key vector.1487 pub fn bytes_keys_to_property_keys(1488 keys: Vec<Vec<u8>>,1489 ) -> Result<Vec<PropertyKey>, DispatchError> {1490 keys.into_iter()1491 .map(|key| -> Result<PropertyKey, DispatchError> {1492 key.try_into()1493 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())1494 })1495 .collect::<Result<Vec<PropertyKey>, DispatchError>>()1496 }14971498 /// Get properties according to given keys.1499 pub fn filter_collection_properties(1500 collection_id: CollectionId,1501 keys: Option<Vec<PropertyKey>>,1502 ) -> Result<Vec<Property>, DispatchError> {1503 let properties = Self::collection_properties(collection_id);15041505 let properties = keys1506 .map(|keys| {1507 keys.into_iter()1508 .filter_map(|key| {1509 properties.get(&key).map(|value| Property {1510 key,1511 value: value.clone(),1512 })1513 })1514 .collect()1515 })1516 .unwrap_or_else(|| {1517 properties1518 .into_iter()1519 .map(|(key, value)| Property { key, value })1520 .collect()1521 });15221523 Ok(properties)1524 }15251526 /// Get property permissions according to given keys.1527 pub fn filter_property_permissions(1528 collection_id: CollectionId,1529 keys: Option<Vec<PropertyKey>>,1530 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {1531 let permissions = Self::property_permissions(collection_id);15321533 let key_permissions = keys1534 .map(|keys| {1535 keys.into_iter()1536 .filter_map(|key| {1537 permissions1538 .get(&key)1539 .map(|permission| PropertyKeyPermission {1540 key,1541 permission: permission.clone(),1542 })1543 })1544 .collect()1545 })1546 .unwrap_or_else(|| {1547 permissions1548 .into_iter()1549 .map(|(key, permission)| PropertyKeyPermission { key, permission })1550 .collect()1551 });15521553 Ok(key_permissions)1554 }15551556 /// Toggle `user` participation in the `collection`'s allow list.1557 /// #### Store read/writes1558 /// 1 writes1559 pub fn toggle_allowlist(1560 collection: &CollectionHandle<T>,1561 sender: &T::CrossAccountId,1562 user: &T::CrossAccountId,1563 allowed: bool,1564 ) -> DispatchResult {1565 collection.check_is_owner_or_admin(sender)?;15661567 // =========15681569 if allowed {1570 <Allowlist<T>>::insert((collection.id, user), true);1571 Self::deposit_event(Event::<T>::AllowListAddressAdded(1572 collection.id,1573 user.clone(),1574 ));1575 } else {1576 <Allowlist<T>>::remove((collection.id, user));1577 Self::deposit_event(Event::<T>::AllowListAddressRemoved(1578 collection.id,1579 user.clone(),1580 ));1581 }15821583 <PalletEvm<T>>::deposit_log(1584 erc::CollectionHelpersEvents::CollectionChanged {1585 collection_id: eth::collection_id_to_address(collection.id),1586 }1587 .to_log(T::ContractAddress::get()),1588 );15891590 Ok(())1591 }15921593 /// Toggle `user` participation in the `collection`'s admin list.1594 /// #### Store read/writes1595 /// 2 reads, 2 writes1596 pub fn toggle_admin(1597 collection: &CollectionHandle<T>,1598 sender: &T::CrossAccountId,1599 user: &T::CrossAccountId,1600 admin: bool,1601 ) -> DispatchResult {1602 collection.check_is_internal()?;1603 collection.check_is_owner(sender)?;16041605 let is_admin = <IsAdmin<T>>::get((collection.id, user));1606 if is_admin == admin {1607 if admin {1608 return Ok(());1609 } else {1610 return Err(Error::<T>::UserIsNotCollectionAdmin.into());1611 }1612 }1613 let amount = <AdminAmount<T>>::get(collection.id);16141615 // =========16161617 if admin {1618 let amount = amount1619 .checked_add(1)1620 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1621 ensure!(1622 amount <= Self::collection_admins_limit(),1623 <Error<T>>::CollectionAdminCountExceeded,1624 );16251626 <AdminAmount<T>>::insert(collection.id, amount);1627 <IsAdmin<T>>::insert((collection.id, user), true);16281629 Self::deposit_event(Event::<T>::CollectionAdminAdded(1630 collection.id,1631 user.clone(),1632 ));1633 } else {1634 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1635 <IsAdmin<T>>::remove((collection.id, user));16361637 Self::deposit_event(Event::<T>::CollectionAdminRemoved(1638 collection.id,1639 user.clone(),1640 ));1641 }16421643 <PalletEvm<T>>::deposit_log(1644 erc::CollectionHelpersEvents::CollectionChanged {1645 collection_id: eth::collection_id_to_address(collection.id),1646 }1647 .to_log(T::ContractAddress::get()),1648 );16491650 Ok(())1651 }16521653 /// Update collection limits.1654 pub fn update_limits(1655 user: &T::CrossAccountId,1656 collection: &mut CollectionHandle<T>,1657 new_limit: CollectionLimits,1658 ) -> DispatchResult {1659 collection.check_is_internal()?;1660 collection.check_is_owner_or_admin(user)?;16611662 collection.limits =1663 Self::clamp_limits(collection.mode.clone(), &collection.limits, new_limit)?;16641665 Self::deposit_event(Event::<T>::CollectionLimitSet(collection.id));1666 <PalletEvm<T>>::deposit_log(1667 erc::CollectionHelpersEvents::CollectionChanged {1668 collection_id: eth::collection_id_to_address(collection.id),1669 }1670 .to_log(T::ContractAddress::get()),1671 );16721673 collection.save()1674 }16751676 /// Merge set fields from `new_limit` to `old_limit`.1677 fn clamp_limits(1678 mode: CollectionMode,1679 old_limit: &CollectionLimits,1680 mut new_limit: CollectionLimits,1681 ) -> Result<CollectionLimits, DispatchError> {1682 let limits = old_limit;1683 limit_default!(old_limit, new_limit,1684 account_token_ownership_limit => ensure!(1685 new_limit <= MAX_TOKEN_OWNERSHIP,1686 <Error<T>>::CollectionLimitBoundsExceeded,1687 ),1688 sponsored_data_size => ensure!(1689 new_limit <= CUSTOM_DATA_LIMIT,1690 <Error<T>>::CollectionLimitBoundsExceeded,1691 ),16921693 sponsored_data_rate_limit => {},1694 token_limit => ensure!(1695 old_limit >= new_limit && new_limit > 0,1696 <Error<T>>::CollectionTokenLimitExceeded1697 ),16981699 sponsor_transfer_timeout(match mode {1700 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1701 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1702 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1703 }) => ensure!(1704 new_limit <= MAX_SPONSOR_TIMEOUT,1705 <Error<T>>::CollectionLimitBoundsExceeded,1706 ),1707 sponsor_approve_timeout => {},1708 owner_can_transfer => ensure!(1709 !limits.owner_can_transfer_instaled() ||1710 old_limit || !new_limit,1711 <Error<T>>::OwnerPermissionsCantBeReverted,1712 ),1713 owner_can_destroy => ensure!(1714 old_limit || !new_limit,1715 <Error<T>>::OwnerPermissionsCantBeReverted,1716 ),1717 transfers_enabled => {},1718 );1719 Ok(new_limit)1720 }17211722 /// Update collection permissions.1723 pub fn update_permissions(1724 user: &T::CrossAccountId,1725 collection: &mut CollectionHandle<T>,1726 new_permission: CollectionPermissions,1727 ) -> DispatchResult {1728 collection.check_is_internal()?;1729 collection.check_is_owner_or_admin(user)?;1730 collection.permissions = Self::clamp_permissions(1731 collection.mode.clone(),1732 &collection.permissions,1733 new_permission,1734 )?;17351736 Self::deposit_event(Event::<T>::CollectionPermissionSet(collection.id));1737 <PalletEvm<T>>::deposit_log(1738 erc::CollectionHelpersEvents::CollectionChanged {1739 collection_id: eth::collection_id_to_address(collection.id),1740 }1741 .to_log(T::ContractAddress::get()),1742 );17431744 collection.save()1745 }17461747 /// Merge set fields from `new_permission` to `old_permission`.1748 fn clamp_permissions(1749 _mode: CollectionMode,1750 old_permission: &CollectionPermissions,1751 mut new_permission: CollectionPermissions,1752 ) -> Result<CollectionPermissions, DispatchError> {1753 limit_default_clone!(old_permission, new_permission,1754 access => {},1755 mint_mode => {},1756 nesting => { /* todo check for permissive, if only it gets out of benchmarks */ },1757 );1758 Ok(new_permission)1759 }17601761 /// Repair possibly broken properties of a collection.1762 pub fn repair_collection(collection_id: CollectionId) -> DispatchResult {1763 CollectionProperties::<T>::mutate(collection_id, |properties| {1764 properties.recompute_consumed_space();1765 });17661767 Ok(())1768 }1769}17701771/// Indicates unsupported methods by returning [Error::UnsupportedOperation].1772#[macro_export]1773macro_rules! unsupported {1774 ($runtime:path) => {1775 Err($crate::Error::<$runtime>::UnsupportedOperation.into())1776 };1777}17781779/// Return weights for various worst-case operations.1780pub trait CommonWeightInfo<CrossAccountId> {1781 /// Weight of item creation.1782 fn create_item() -> Weight;17831784 /// Weight of items creation.1785 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;17861787 /// Weight of items creation.1788 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;17891790 /// The weight of the burning item.1791 fn burn_item() -> Weight;17921793 /// Property setting weight.1794 ///1795 /// * `amount`- The number of properties to set.1796 fn set_collection_properties(amount: u32) -> Weight;17971798 /// Collection property deletion weight.1799 ///1800 /// * `amount`- The number of properties to set.1801 fn delete_collection_properties(amount: u32) -> Weight;18021803 /// Token property setting weight.1804 ///1805 /// * `amount`- The number of properties to set.1806 fn set_token_properties(amount: u32) -> Weight;18071808 /// Token property deletion weight.1809 ///1810 /// * `amount`- The number of properties to delete.1811 fn delete_token_properties(amount: u32) -> Weight;18121813 /// Token property permissions set weight.1814 ///1815 /// * `amount`- The number of property permissions to set.1816 fn set_token_property_permissions(amount: u32) -> Weight;18171818 /// Transfer price of the token or its parts.1819 fn transfer() -> Weight;18201821 /// The price of setting the permission of the operation from another user.1822 fn approve() -> Weight;18231824 /// The price of setting the permission of the operation from another user for eth mirror.1825 fn approve_from() -> Weight;18261827 /// Transfer price from another user.1828 fn transfer_from() -> Weight;18291830 /// The price of burning a token from another user.1831 fn burn_from() -> Weight;18321833 /// Differs from burn_item in case of Fungible and Refungible, as it should burn1834 /// whole users's balance.1835 ///1836 /// This method shouldn't be used directly, as it doesn't count breadth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1837 fn burn_recursively_self_raw() -> Weight;18381839 /// Cost of iterating over `amount` children while burning, without counting child burning itself.1840 ///1841 /// This method shouldn't be used directly, as it doesn't count depth price, use [burn_recursively](CommonWeightInfo::burn_recursively) instead1842 fn burn_recursively_breadth_raw(amount: u32) -> Weight;18431844 /// The price of recursive burning a token.1845 ///1846 /// `max_selfs` - The maximum burning weight of the token itself.1847 /// `max_breadth` - The maximum number of nested tokens to burn.1848 fn burn_recursively(max_selfs: u32, max_breadth: u32) -> Weight {1849 Self::burn_recursively_self_raw()1850 .saturating_mul(max_selfs.max(1) as u64)1851 .saturating_add(Self::burn_recursively_breadth_raw(max_breadth))1852 }18531854 /// The price of retrieving token owner1855 fn token_owner() -> Weight;18561857 /// The price of setting approval for all1858 fn set_allowance_for_all() -> Weight;18591860 /// The price of repairing an item.1861 fn force_repair_item() -> Weight;1862}18631864/// Weight info extension trait for refungible pallet.1865pub trait RefungibleExtensionsWeightInfo {1866 /// Weight of token repartition.1867 fn repartition() -> Weight;1868}18691870/// Common collection operations.1871///1872/// It wraps methods in Fungible, Nonfungible and Refungible pallets1873/// and adds weight info.1874pub trait CommonCollectionOperations<T: Config> {1875 /// Create token.1876 ///1877 /// * `sender` - The user who mint the token and pays for the transaction.1878 /// * `to` - The user who will own the token.1879 /// * `data` - Token data.1880 /// * `nesting_budget` - A budget that can be spent on nesting tokens.1881 fn create_item(1882 &self,1883 sender: T::CrossAccountId,1884 to: T::CrossAccountId,1885 data: CreateItemData,1886 nesting_budget: &dyn Budget,1887 ) -> DispatchResultWithPostInfo;18881889 /// Create multiple tokens.1890 ///1891 /// * `sender` - The user who mint the token and pays for the transaction.1892 /// * `to` - The user who will own the token.1893 /// * `data` - Token data.1894 /// * `nesting_budget` - A budget that can be spent on nesting tokens.1895 fn create_multiple_items(1896 &self,1897 sender: T::CrossAccountId,1898 to: T::CrossAccountId,1899 data: Vec<CreateItemData>,1900 nesting_budget: &dyn Budget,1901 ) -> DispatchResultWithPostInfo;19021903 /// Create multiple tokens.1904 ///1905 /// * `sender` - The user who mint the token and pays for the transaction.1906 /// * `to` - The user who will own the token.1907 /// * `data` - Token data.1908 /// * `nesting_budget` - A budget that can be spent on nesting tokens.1909 fn create_multiple_items_ex(1910 &self,1911 sender: T::CrossAccountId,1912 data: CreateItemExData<T::CrossAccountId>,1913 nesting_budget: &dyn Budget,1914 ) -> DispatchResultWithPostInfo;19151916 /// Burn token.1917 ///1918 /// * `sender` - The user who owns the token.1919 /// * `token` - Token id that will burned.1920 /// * `amount` - The number of parts of the token that will be burned.1921 fn burn_item(1922 &self,1923 sender: T::CrossAccountId,1924 token: TokenId,1925 amount: u128,1926 ) -> DispatchResultWithPostInfo;19271928 /// Burn token and all nested tokens recursievly.1929 ///1930 /// * `sender` - The user who owns the token.1931 /// * `token` - Token id that will burned.1932 /// * `self_budget` - The budget that can be spent on burning tokens.1933 /// * `breadth_budget` - The budget that can be spent on burning nested tokens.1934 fn burn_item_recursively(1935 &self,1936 sender: T::CrossAccountId,1937 token: TokenId,1938 self_budget: &dyn Budget,1939 breadth_budget: &dyn Budget,1940 ) -> DispatchResultWithPostInfo;19411942 /// Set collection properties.1943 ///1944 /// * `sender` - Must be either the owner of the collection or its admin.1945 /// * `properties` - Properties to be set.1946 fn set_collection_properties(1947 &self,1948 sender: T::CrossAccountId,1949 properties: Vec<Property>,1950 ) -> DispatchResultWithPostInfo;19511952 /// Delete collection properties.1953 ///1954 /// * `sender` - Must be either the owner of the collection or its admin.1955 /// * `properties` - The properties to be removed.1956 fn delete_collection_properties(1957 &self,1958 sender: &T::CrossAccountId,1959 property_keys: Vec<PropertyKey>,1960 ) -> DispatchResultWithPostInfo;19611962 /// Set token properties.1963 ///1964 /// The appropriate [`PropertyPermission`] for the token property1965 /// must be set with [`Self::set_token_property_permissions`].1966 ///1967 /// * `sender` - Must be either the owner of the token or its admin.1968 /// * `token_id` - The token for which the properties are being set.1969 /// * `properties` - Properties to be set.1970 /// * `budget` - Budget for setting properties.1971 fn set_token_properties(1972 &self,1973 sender: T::CrossAccountId,1974 token_id: TokenId,1975 properties: Vec<Property>,1976 budget: &dyn Budget,1977 ) -> DispatchResultWithPostInfo;19781979 /// Remove token properties.1980 ///1981 /// The appropriate [`PropertyPermission`] for the token property1982 /// must be set with [`Self::set_token_property_permissions`].1983 ///1984 /// * `sender` - Must be either the owner of the token or its admin.1985 /// * `token_id` - The token for which the properties are being remove.1986 /// * `property_keys` - Keys to remove corresponding properties.1987 /// * `budget` - Budget for removing properties.1988 fn delete_token_properties(1989 &self,1990 sender: T::CrossAccountId,1991 token_id: TokenId,1992 property_keys: Vec<PropertyKey>,1993 budget: &dyn Budget,1994 ) -> DispatchResultWithPostInfo;19951996 /// Set token property permissions.1997 ///1998 /// * `sender` - Must be either the owner of the token or its admin.1999 /// * `token_id` - The token for which the properties are being set.2000 /// * `property_permissions` - Property permissions to be set.2001 /// * `budget` - Budget for setting properties.2002 fn set_token_property_permissions(2003 &self,2004 sender: &T::CrossAccountId,2005 property_permissions: Vec<PropertyKeyPermission>,2006 ) -> DispatchResultWithPostInfo;20072008 /// Transfer amount of token pieces.2009 ///2010 /// * `sender` - Donor user.2011 /// * `to` - Recepient user.2012 /// * `token` - The token of which parts are being sent.2013 /// * `amount` - The number of parts of the token that will be transferred.2014 /// * `budget` - The maximum budget that can be spent on the transfer.2015 fn transfer(2016 &self,2017 sender: T::CrossAccountId,2018 to: T::CrossAccountId,2019 token: TokenId,2020 amount: u128,2021 budget: &dyn Budget,2022 ) -> DispatchResultWithPostInfo;20232024 /// Grant access to another account to transfer parts of the token owned by the calling user via [Self::transfer_from].2025 ///2026 /// * `sender` - The user who grants access to the token.2027 /// * `spender` - The user to whom the rights are granted.2028 /// * `token` - The token to which access is granted.2029 /// * `amount` - The amount of pieces that another user can dispose of.2030 fn approve(2031 &self,2032 sender: T::CrossAccountId,2033 spender: T::CrossAccountId,2034 token: TokenId,2035 amount: u128,2036 ) -> DispatchResultWithPostInfo;20372038 /// Grant access to another account to transfer parts of the token owned by the calling user's eth mirror via [Self::transfer_from].2039 ///2040 /// * `sender` - The user who grants access to the token.2041 /// * `from` - Spender's eth mirror.2042 /// * `to` - The user to whom the rights are granted.2043 /// * `token` - The token to which access is granted.2044 /// * `amount` - The amount of pieces that another user can dispose of.2045 fn approve_from(2046 &self,2047 sender: T::CrossAccountId,2048 from: T::CrossAccountId,2049 to: T::CrossAccountId,2050 token: TokenId,2051 amount: u128,2052 ) -> DispatchResultWithPostInfo;20532054 /// Send parts of a token owned by another user.2055 ///2056 /// Before calling this method, you must grant rights to the calling user via [`Self::approve`].2057 ///2058 /// * `sender` - The user who must have access to the token (see [`Self::approve`]).2059 /// * `from` - The user who owns the token.2060 /// * `to` - Recepient user.2061 /// * `token` - The token of which parts are being sent.2062 /// * `amount` - The number of parts of the token that will be transferred.2063 /// * `budget` - The maximum budget that can be spent on the transfer.2064 fn transfer_from(2065 &self,2066 sender: T::CrossAccountId,2067 from: T::CrossAccountId,2068 to: T::CrossAccountId,2069 token: TokenId,2070 amount: u128,2071 budget: &dyn Budget,2072 ) -> DispatchResultWithPostInfo;20732074 /// Burn parts of a token owned by another user.2075 ///2076 /// Before calling this method, you must grant rights to the calling user via [`Self::approve`].2077 ///2078 /// * `sender` - The user who must have access to the token (see [`Self::approve`]).2079 /// * `from` - The user who owns the token.2080 /// * `token` - The token of which parts are being sent.2081 /// * `amount` - The number of parts of the token that will be transferred.2082 /// * `budget` - The maximum budget that can be spent on the burn.2083 fn burn_from(2084 &self,2085 sender: T::CrossAccountId,2086 from: T::CrossAccountId,2087 token: TokenId,2088 amount: u128,2089 budget: &dyn Budget,2090 ) -> DispatchResultWithPostInfo;20912092 /// Check permission to nest token.2093 ///2094 /// * `sender` - The user who initiated the check.2095 /// * `from` - The token that is checked for embedding.2096 /// * `under` - Token under which to check.2097 /// * `budget` - The maximum budget that can be spent on the check.2098 fn check_nesting(2099 &self,2100 sender: T::CrossAccountId,2101 from: (CollectionId, TokenId),2102 under: TokenId,2103 budget: &dyn Budget,2104 ) -> DispatchResult;21052106 /// Nest one token into another.2107 ///2108 /// * `under` - Token holder.2109 /// * `to_nest` - Nested token.2110 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21112112 /// Unnest token.2113 ///2114 /// * `under` - Token holder.2115 /// * `to_nest` - Token to unnest.2116 fn unnest(&self, under: TokenId, to_nest: (CollectionId, TokenId));21172118 /// Get all user tokens.2119 ///2120 /// * `account` - Account for which you need to get tokens.2121 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;21222123 /// Get all the tokens in the collection.2124 fn collection_tokens(&self) -> Vec<TokenId>;21252126 /// Check if the token exists.2127 ///2128 /// * `token` - Id token to check.2129 fn token_exists(&self, token: TokenId) -> bool;21302131 /// Get the id of the last minted token.2132 fn last_token_id(&self) -> TokenId;21332134 /// Get the owner of the token.2135 ///2136 /// * `token` - The token for which you need to find out the owner.2137 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;21382139 /// Returns 10 tokens owners in no particular order.2140 ///2141 /// * `token` - The token for which you need to find out the owners.2142 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;21432144 /// Get the value of the token property by key.2145 ///2146 /// * `token` - Token with the property to get.2147 /// * `key` - Property name.2148 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;21492150 /// Get a set of token properties by key vector.2151 ///2152 /// * `token` - Token with the property to get.2153 /// * `keys` - Vector of property keys. If this parameter is [None](sp_std::result::Result),2154 /// then all properties are returned.2155 fn token_properties(&self, token: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;21562157 /// Amount of unique collection tokens2158 fn total_supply(&self) -> u32;21592160 /// Amount of different tokens account has.2161 ///2162 /// * `account` - The account for which need to get the balance.2163 fn account_balance(&self, account: T::CrossAccountId) -> u32;21642165 /// Amount of specific token account have.2166 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;21672168 /// Amount of token pieces2169 fn total_pieces(&self, token: TokenId) -> Option<u128>;21702171 /// Get the number of parts of the token that a trusted user can manage.2172 ///2173 /// * `sender` - Trusted user.2174 /// * `spender` - Owner of the token.2175 /// * `token` - The token for which to get the value.2176 fn allowance(2177 &self,2178 sender: T::CrossAccountId,2179 spender: T::CrossAccountId,2180 token: TokenId,2181 ) -> u128;21822183 /// Get extension for RFT collection.2184 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;21852186 /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.2187 /// * `owner` - Token owner2188 /// * `operator` - Operator2189 /// * `approve` - Should operator status be granted or revoked?2190 fn set_allowance_for_all(2191 &self,2192 owner: T::CrossAccountId,2193 operator: T::CrossAccountId,2194 approve: bool,2195 ) -> DispatchResultWithPostInfo;21962197 /// Tells whether the given `owner` approves the `operator`.2198 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;21992200 /// Repairs a possibly broken item.2201 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo;2202}22032204/// Extension for RFT collection.2205pub trait RefungibleExtensions<T>2206where2207 T: Config,2208{2209 /// Change the number of parts of the token.2210 ///2211 /// When the value changes down, this function is equivalent to burning parts of the token.2212 ///2213 /// * `sender` - The user calling the repartition operation. Must be the owner of the token.2214 /// * `token` - The token for which you want to change the number of parts.2215 /// * `amount` - The new value of the parts of the token.2216 fn repartition(2217 &self,2218 sender: &T::CrossAccountId,2219 token: TokenId,2220 amount: u128,2221 ) -> DispatchResultWithPostInfo;2222}22232224/// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].2225///2226/// Used for [`CommonCollectionOperations`] implementations and flexible enough to do so.2227pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {2228 let post_info = PostDispatchInfo {2229 actual_weight: Some(weight),2230 pays_fee: Pays::Yes,2231 };2232 match res {2233 Ok(()) => Ok(post_info),2234 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),2235 }2236}22372238impl<T: Config> From<PropertiesError> for Error<T> {2239 fn from(error: PropertiesError) -> Self {2240 match error {2241 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,2242 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,2243 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,2244 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,2245 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,2246 }2247 }2248}pallets/evm-coder-substrate/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-coder-substrate/src/lib.rs
+++ b/pallets/evm-coder-substrate/src/lib.rs
@@ -41,7 +41,7 @@
use evm_coder::{
abi::{AbiReader, AbiWrite, AbiWriter},
execution,
- types::{Msg, value},
+ types::{Msg, Value},
};
pub use pallet::*;
@@ -256,7 +256,7 @@
>(
caller: H160,
e: &mut E,
- value: value,
+ value: Value,
input: &[u8],
) -> execution::Result<Option<AbiWriter>> {
let (selector, mut reader) = AbiReader::new_call(input)?;
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -49,25 +49,25 @@
ContractSponsorSet {
/// Contract address of the affected collection.
#[indexed]
- contract_address: address,
+ contract_address: Address,
/// New sponsor address.
- sponsor: address,
+ sponsor: Address,
},
/// New sponsor was confirm.
ContractSponsorshipConfirmed {
/// Contract address of the affected collection.
#[indexed]
- contract_address: address,
+ contract_address: Address,
/// New sponsor address.
- sponsor: address,
+ sponsor: Address,
},
/// Collection sponsor was removed.
ContractSponsorRemoved {
/// Contract address of the affected collection.
#[indexed]
- contract_address: address,
+ contract_address: Address,
},
}
@@ -96,7 +96,7 @@
/// @dev Returns zero address if contract does not exists
/// @param contractAddress Contract to get owner of
/// @return address Owner of contract
- fn contract_owner(&self, contract_address: address) -> Result<address> {
+ fn contract_owner(&self, contract_address: Address) -> Result<Address> {
Ok(<Owner<T>>::get(contract_address))
}
@@ -105,10 +105,10 @@
/// @param sponsor User address who set as pending sponsor.
fn set_sponsor(
&mut self,
- caller: caller,
- contract_address: address,
- sponsor: address,
- ) -> Result<void> {
+ caller: Caller,
+ contract_address: Address,
+ sponsor: Address,
+ ) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -125,7 +125,7 @@
/// Set contract as self sponsored.
///
/// @param contractAddress Contract for which a self sponsoring is being enabled.
- fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {
+ fn self_sponsored_enable(&mut self, caller: Caller, contract_address: Address) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -146,7 +146,7 @@
/// Remove sponsor.
///
/// @param contractAddress Contract for which a sponsorship is being removed.
- fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {
+ fn remove_sponsor(&mut self, caller: Caller, contract_address: Address) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -161,7 +161,7 @@
/// @dev Caller must be same that set via [`setSponsor`].
///
/// @param contractAddress Сontract for which need to confirm sponsorship.
- fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {
+ fn confirm_sponsorship(&mut self, caller: Caller, contract_address: Address) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -175,7 +175,7 @@
///
/// @param contractAddress The contract for which a sponsor is requested.
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
- fn sponsor(&self, contract_address: address) -> Result<Option<eth::CrossAddress>> {
+ fn sponsor(&self, contract_address: Address) -> Result<Option<eth::CrossAddress>> {
Ok(match Pallet::<T>::get_sponsor(contract_address) {
Some(ref value) => Some(eth::CrossAddress::from_sub_cross_account::<T>(value)),
None => None,
@@ -186,7 +186,7 @@
///
/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.
/// @return **true** if contract has confirmed sponsor.
- fn has_sponsor(&self, contract_address: address) -> Result<bool> {
+ fn has_sponsor(&self, contract_address: Address) -> Result<bool> {
Ok(Pallet::<T>::get_sponsor(contract_address).is_some())
}
@@ -194,23 +194,23 @@
///
/// @param contractAddress The contract for which the presence of a pending sponsor is checked.
/// @return **true** if contract has pending sponsor.
- fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {
+ fn has_pending_sponsor(&self, contract_address: Address) -> Result<bool> {
Ok(match Sponsoring::<T>::get(contract_address) {
SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,
SponsorshipState::Unconfirmed(_) => true,
})
}
- fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
+ fn sponsoring_enabled(&self, contract_address: Address) -> Result<bool> {
Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)
}
fn set_sponsoring_mode(
&mut self,
- caller: caller,
- contract_address: address,
+ caller: Caller,
+ contract_address: Address,
mode: SponsoringModeT,
- ) -> Result<void> {
+ ) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -223,7 +223,7 @@
/// Get current contract sponsoring rate limit
/// @param contractAddress Contract to get sponsoring rate limit of
/// @return uint32 Amount of blocks between two sponsored transactions
- fn sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {
+ fn sponsoring_rate_limit(&self, contract_address: Address) -> Result<u32> {
self.recorder().consume_sload()?;
Ok(<SponsoringRateLimit<T>>::get(contract_address)
@@ -239,10 +239,10 @@
/// @dev Only contract owner can change this setting
fn set_sponsoring_rate_limit(
&mut self,
- caller: caller,
- contract_address: address,
- rate_limit: uint32,
- ) -> Result<void> {
+ caller: Caller,
+ contract_address: Address,
+ rate_limit: u32,
+ ) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -259,10 +259,10 @@
/// @dev Only contract owner can change this setting
fn set_sponsoring_fee_limit(
&mut self,
- caller: caller,
- contract_address: address,
- fee_limit: uint256,
- ) -> Result<void> {
+ caller: Caller,
+ contract_address: Address,
+ fee_limit: U256,
+ ) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -276,7 +276,7 @@
/// @param contractAddress Contract to get sponsoring fee limit of
/// @return uint256 Maximum amount of fee that could be spent by single
/// transaction
- fn sponsoring_fee_limit(&self, contract_address: address) -> Result<uint256> {
+ fn sponsoring_fee_limit(&self, contract_address: Address) -> Result<U256> {
self.recorder().consume_sload()?;
Ok(get_sponsoring_fee_limit::<T>(contract_address))
@@ -287,7 +287,7 @@
/// @param contractAddress Contract to check allowlist of
/// @param user User to check
/// @return bool Is specified users exists in contract allowlist
- fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
+ fn allowed(&self, contract_address: Address, user: Address) -> Result<bool> {
self.0.consume_sload()?;
Ok(<Pallet<T>>::allowed(contract_address, user))
}
@@ -300,11 +300,11 @@
/// @dev Only contract owner can change this setting
fn toggle_allowed(
&mut self,
- caller: caller,
- contract_address: address,
- user: address,
+ caller: Caller,
+ contract_address: Address,
+ user: Address,
is_allowed: bool,
- ) -> Result<void> {
+ ) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -320,7 +320,7 @@
/// in case of allowlist access enabled, only users from allowlist may call this contract
/// @param contractAddress Contract to get allowlist access of
/// @return bool Is specified contract has allowlist access enabled
- fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
+ fn allowlist_enabled(&self, contract_address: Address) -> Result<bool> {
Ok(<AllowlistEnabled<T>>::get(contract_address))
}
@@ -329,10 +329,10 @@
/// @param enabled Should allowlist access to be enabled?
fn toggle_allowlist(
&mut self,
- caller: caller,
- contract_address: address,
+ caller: Caller,
+ contract_address: Address,
enabled: bool,
- ) -> Result<void> {
+ ) -> Result<()> {
self.recorder().consume_sload()?;
self.recorder().consume_sstore()?;
@@ -441,7 +441,7 @@
}
}
-fn get_sponsoring_fee_limit<T: Config>(contract_address: address) -> uint256 {
+fn get_sponsoring_fee_limit<T: Config>(contract_address: Address) -> U256 {
<SponsoringFeeLimit<T>>::get(contract_address)
.get(&0xffffffff)
.cloned()
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -69,7 +69,7 @@
}
#[pallet::pallet]
- #[pallet::generate_store(pub(super) trait Store)]
+ #[pallet::generate_store(trait Store)]
pub struct Pallet<T>(_);
/// Store owner for contract.
@@ -80,9 +80,9 @@
pub(super) type Owner<T: Config> =
StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;
+ /// Deprecated: this storage is deprecated
#[pallet::storage]
- #[deprecated]
- pub(super) type SelfSponsoring<T: Config> =
+ type SelfSponsoring<T: Config> =
StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;
/// Store for contract sponsorship state.
@@ -349,6 +349,7 @@
}
/// Get current sponsoring mode, performing lazy migration from legacy storage
+ /// Deprecated: this method is for deprecated storage
pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {
<SponsoringMode<T>>::get(contract)
.or_else(|| {
@@ -359,6 +360,7 @@
}
/// Reconfigure contract sponsoring mode
+ /// Deprecated: this method is for deprecated storage
pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {
if mode == SponsoringModeT::Disabled {
<SponsoringMode<T>>::remove(contract);
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -32,7 +32,7 @@
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::{call, dispatch_to_evm};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use sp_core::Get;
+use sp_core::{U256, Get};
use crate::{
Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,
@@ -43,50 +43,50 @@
pub enum ERC20Events {
Transfer {
#[indexed]
- from: address,
+ from: Address,
#[indexed]
- to: address,
- value: uint256,
+ to: Address,
+ value: U256,
},
Approval {
#[indexed]
- owner: address,
+ owner: Address,
#[indexed]
- spender: address,
- value: uint256,
+ spender: Address,
+ value: U256,
},
}
#[solidity_interface(name = ERC20, events(ERC20Events))]
impl<T: Config> FungibleHandle<T> {
- fn name(&self) -> Result<string> {
+ fn name(&self) -> Result<String> {
Ok(decode_utf16(self.name.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
- fn symbol(&self) -> Result<string> {
- Ok(string::from_utf8_lossy(&self.token_prefix).into())
+ fn symbol(&self) -> Result<String> {
+ Ok(String::from_utf8_lossy(&self.token_prefix).into())
}
- fn total_supply(&self) -> Result<uint256> {
+ fn total_supply(&self) -> Result<U256> {
self.consume_store_reads(1)?;
Ok(<TotalSupply<T>>::get(self.id).into())
}
- fn decimals(&self) -> Result<uint8> {
+ fn decimals(&self) -> Result<u8> {
Ok(if let CollectionMode::Fungible(decimals) = &self.mode {
*decimals
} else {
unreachable!()
})
}
- fn balance_of(&self, owner: address) -> Result<uint256> {
+ fn balance_of(&self, owner: Address) -> Result<U256> {
self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let balance = <Balance<T>>::get((self.id, owner));
Ok(balance.into())
}
#[weight(<SelfWeightOf<T>>::transfer())]
- fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
+ fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -101,10 +101,10 @@
#[weight(<SelfWeightOf<T>>::transfer_from())]
fn transfer_from(
&mut self,
- caller: caller,
- from: address,
- to: address,
- amount: uint256,
+ caller: Caller,
+ from: Address,
+ to: Address,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
@@ -119,7 +119,7 @@
Ok(true)
}
#[weight(<SelfWeightOf<T>>::approve())]
- fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {
+ fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let spender = T::CrossAccountId::from_eth(spender);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -128,7 +128,7 @@
.map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
- fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
+ fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {
self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let spender = T::CrossAccountId::from_eth(spender);
@@ -137,7 +137,7 @@
}
/// @notice Returns collection helper contract address
- fn collection_helper_address(&self) -> Result<address> {
+ fn collection_helper_address(&self) -> Result<Address> {
Ok(T::ContractAddress::get())
}
}
@@ -148,7 +148,7 @@
/// @param to account that will receive minted tokens
/// @param amount amount of tokens to mint
#[weight(<SelfWeightOf<T>>::create_item())]
- fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
+ fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -167,18 +167,18 @@
T::AccountId: From<[u8; 32]>,
{
/// @notice A description for the collection.
- fn description(&self) -> Result<string> {
+ fn description(&self) -> Result<String> {
Ok(decode_utf16(self.description.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
@@ -194,9 +194,9 @@
#[weight(<SelfWeightOf<T>>::approve())]
fn approve_cross(
&mut self,
- caller: caller,
+ caller: Caller,
spender: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let spender = spender.into_sub_cross_account::<T>()?;
@@ -214,7 +214,7 @@
/// @param amount The amount that will be burnt.
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::burn_from())]
- fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
+ fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -235,9 +235,9 @@
#[weight(<SelfWeightOf<T>>::burn_from())]
fn burn_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
@@ -254,7 +254,7 @@
/// Mint tokens for multiple accounts.
/// @param amounts array of pairs of account address and amount
#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]
- fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {
+ fn mint_bulk(&mut self, caller: Caller, amounts: Vec<(Address, U256)>) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let budget = self
.recorder
@@ -277,9 +277,9 @@
#[weight(<SelfWeightOf<T>>::transfer())]
fn transfer_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
@@ -295,10 +295,10 @@
#[weight(<SelfWeightOf<T>>::transfer_from())]
fn transfer_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: pallet_common::eth::CrossAddress,
to: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -274,11 +274,9 @@
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
/// @dev EVM selector for this function is: 0x41835d4c,
/// or in textual repr: setCollectionAccess(uint8)
- function setCollectionAccess(uint8 mode) public {
+ function setCollectionAccess(AccessMode mode) public {
require(false, stub_error);
mode;
dummy = 0;
@@ -443,6 +441,14 @@
uint256 sub;
}
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList
+}
+
/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
struct CollectionNestingPermission {
CollectionPermissionField field;
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -43,7 +43,7 @@
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::call;
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use sp_core::Get;
+use sp_core::{U256, Get};
use crate::{
AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,
@@ -63,8 +63,8 @@
#[solidity(hide)]
fn set_token_property_permission(
&mut self,
- caller: caller,
- key: string,
+ caller: Caller,
+ key: String,
is_mutable: bool,
collection_admin: bool,
token_owner: bool,
@@ -93,7 +93,7 @@
#[weight(<SelfWeightOf<T>>::set_token_property_permissions(permissions.len() as u32))]
fn set_token_property_permissions(
&mut self,
- caller: caller,
+ caller: Caller,
permissions: Vec<eth::TokenPropertyPermission>,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
@@ -121,10 +121,10 @@
#[weight(<SelfWeightOf<T>>::set_token_properties(1))]
fn set_property(
&mut self,
- caller: caller,
- token_id: uint256,
- key: string,
- value: bytes,
+ caller: Caller,
+ token_id: U256,
+ key: String,
+ value: Bytes,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
@@ -154,8 +154,8 @@
#[weight(<SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]
fn set_properties(
&mut self,
- caller: caller,
- token_id: uint256,
+ caller: Caller,
+ token_id: U256,
properties: Vec<eth::Property>,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
@@ -187,7 +187,7 @@
/// @param key Property key.
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::delete_token_properties(1))]
- fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {
+ fn delete_property(&mut self, token_id: U256, caller: Caller, key: String) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
let key = <Vec<u8>>::from(key)
@@ -209,9 +209,9 @@
#[weight(<SelfWeightOf<T>>::delete_token_properties(keys.len() as u32))]
fn delete_properties(
&mut self,
- token_id: uint256,
- caller: caller,
- keys: Vec<string>,
+ token_id: U256,
+ caller: Caller,
+ keys: Vec<String>,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
@@ -239,7 +239,7 @@
/// @param tokenId ID of the token.
/// @param key Property key.
/// @return Property value bytes
- fn property(&self, token_id: uint256, key: string) -> Result<bytes> {
+ fn property(&self, token_id: U256, key: String) -> Result<Bytes> {
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
let key = <Vec<u8>>::from(key)
.try_into()
@@ -261,11 +261,11 @@
/// any transfer, the approved address for that NFT (if any) is reset to none.
Transfer {
#[indexed]
- from: address,
+ from: Address,
#[indexed]
- to: address,
+ to: Address,
#[indexed]
- token_id: uint256,
+ token_id: U256,
},
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
@@ -273,30 +273,24 @@
/// address for that NFT (if any) is reset to none.
Approval {
#[indexed]
- owner: address,
+ owner: Address,
#[indexed]
- approved: address,
+ approved: Address,
#[indexed]
- token_id: uint256,
+ token_id: U256,
},
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
#[allow(dead_code)]
ApprovalForAll {
#[indexed]
- owner: address,
+ owner: Address,
#[indexed]
- operator: address,
+ operator: Address,
approved: bool,
},
}
-#[derive(ToLog)]
-pub enum ERC721UniqueMintableEvents {
- #[allow(dead_code)]
- MintingFinished {},
-}
-
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)]
@@ -307,14 +301,14 @@
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev real implementation of this function lies in `ERC721UniqueExtensions`
#[solidity(hide, rename_selector = "name")]
- fn name_proxy(&self) -> Result<string> {
+ fn name_proxy(&self) -> Result<String> {
self.name()
}
/// @notice An abbreviated name for NFTs in this contract
/// @dev real implementation of this function lies in `ERC721UniqueExtensions`
#[solidity(hide, rename_selector = "symbol")]
- fn symbol_proxy(&self) -> Result<string> {
+ fn symbol_proxy(&self) -> Result<String> {
self.symbol()
}
@@ -328,7 +322,7 @@
///
/// @return token's const_metadata
#[solidity(rename_selector = "tokenURI")]
- fn token_uri(&self, token_id: uint256) -> Result<string> {
+ fn token_uri(&self, token_id: U256) -> Result<String> {
let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
match get_token_property(self, token_id_u32, &key::url()).as_deref() {
@@ -341,7 +335,7 @@
let base_uri =
pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())
.map(BoundedVec::into_inner)
- .map(string::from_utf8)
+ .map(String::from_utf8)
.transpose()
.map_err(|e| {
Error::Revert(alloc::format!(
@@ -374,12 +368,12 @@
/// @param index A counter less than `totalSupply()`
/// @return The token identifier for the `index`th NFT,
/// (sort order not specified)
- fn token_by_index(&self, index: uint256) -> Result<uint256> {
+ fn token_by_index(&self, index: U256) -> Result<U256> {
Ok(index)
}
/// @dev Not implemented
- fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
+ fn token_of_owner_by_index(&self, _owner: Address, _index: U256) -> Result<U256> {
// TODO: Not implemetable
Err("not implemented".into())
}
@@ -387,7 +381,7 @@
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
- fn total_supply(&self) -> Result<uint256> {
+ fn total_supply(&self) -> Result<U256> {
self.consume_store_reads(1)?;
Ok(<Pallet<T>>::total_supply(self).into())
}
@@ -402,7 +396,7 @@
/// function throws for queries about the zero address.
/// @param owner An address for whom to query the balance
/// @return The number of NFTs owned by `owner`, possibly zero
- fn balance_of(&self, owner: address) -> Result<uint256> {
+ fn balance_of(&self, owner: Address) -> Result<U256> {
self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let balance = <AccountBalance<T>>::get((self.id, owner));
@@ -413,7 +407,7 @@
/// about them do throw.
/// @param tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
- fn owner_of(&self, token_id: uint256) -> Result<address> {
+ fn owner_of(&self, token_id: U256) -> Result<Address> {
self.consume_store_reads(1)?;
let token: TokenId = token_id.try_into()?;
Ok(*<TokenData<T>>::get((self.id, token))
@@ -425,21 +419,16 @@
#[solidity(rename_selector = "safeTransferFrom")]
fn safe_transfer_from_with_data(
&mut self,
- _from: address,
- _to: address,
- _token_id: uint256,
- _data: bytes,
- ) -> Result<void> {
+ _from: Address,
+ _to: Address,
+ _token_id: U256,
+ _data: Bytes,
+ ) -> Result<()> {
// TODO: Not implemetable
Err("not implemented".into())
}
/// @dev Not implemented
- fn safe_transfer_from(
- &mut self,
- _from: address,
- _to: address,
- _token_id: uint256,
- ) -> Result<void> {
+ fn safe_transfer_from(&mut self, _from: Address, _to: Address, _token_id: U256) -> Result<()> {
// TODO: Not implemetable
Err("not implemented".into())
}
@@ -456,11 +445,11 @@
#[weight(<SelfWeightOf<T>>::transfer_from())]
fn transfer_from(
&mut self,
- caller: caller,
- from: address,
- to: address,
- token_id: uint256,
- ) -> Result<void> {
+ caller: Caller,
+ from: Address,
+ to: Address,
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
let to = T::CrossAccountId::from_eth(to);
@@ -481,7 +470,7 @@
/// @param approved The new approved NFT controller
/// @param tokenId The NFT to approve
#[weight(<SelfWeightOf<T>>::approve())]
- fn approve(&mut self, caller: caller, approved: address, token_id: uint256) -> Result<void> {
+ fn approve(&mut self, caller: Caller, approved: Address, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let approved = T::CrossAccountId::from_eth(approved);
let token = token_id.try_into()?;
@@ -498,10 +487,10 @@
#[weight(<SelfWeightOf<T>>::set_allowance_for_all())]
fn set_approval_for_all(
&mut self,
- caller: caller,
- operator: address,
+ caller: Caller,
+ operator: Address,
approved: bool,
- ) -> Result<void> {
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let operator = T::CrossAccountId::from_eth(operator);
@@ -511,14 +500,14 @@
}
/// @dev Not implemented
- fn get_approved(&self, _token_id: uint256) -> Result<address> {
+ fn get_approved(&self, _token_id: U256) -> Result<Address> {
// TODO: Not implemetable
Err("not implemented".into())
}
/// @notice Tells whether the given `owner` approves the `operator`.
#[weight(<SelfWeightOf<T>>::allowance_for_all())]
- fn is_approved_for_all(&self, owner: address, operator: address) -> Result<bool> {
+ fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {
let owner = T::CrossAccountId::from_eth(owner);
let operator = T::CrossAccountId::from_eth(operator);
@@ -534,7 +523,7 @@
/// operator of the current owner.
/// @param tokenId The NFT to approve
#[weight(<SelfWeightOf<T>>::burn_item())]
- fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
+ fn burn(&mut self, caller: Caller, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token = token_id.try_into()?;
@@ -544,18 +533,14 @@
}
/// @title ERC721 minting logic.
-#[solidity_interface(name = ERC721UniqueMintable, events(ERC721UniqueMintableEvents))]
+#[solidity_interface(name = ERC721UniqueMintable)]
impl<T: Config> NonfungibleHandle<T> {
- fn minting_finished(&self) -> Result<bool> {
- Ok(false)
- }
-
/// @notice Function to mint a token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
#[weight(<SelfWeightOf<T>>::create_item())]
- fn mint(&mut self, caller: caller, to: address) -> Result<uint256> {
- let token_id: uint256 = <TokensMinted<T>>::get(self.id)
+ fn mint(&mut self, caller: Caller, to: Address) -> Result<U256> {
+ let token_id: U256 = <TokensMinted<T>>::get(self.id)
.checked_add(1)
.ok_or("item id overflow")?
.into();
@@ -570,7 +555,7 @@
/// @param tokenId ID of the minted NFT
#[solidity(hide, rename_selector = "mint")]
#[weight(<SelfWeightOf<T>>::create_item())]
- fn mint_check_id(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
+ fn mint_check_id(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let token_id: u32 = token_id.try_into()?;
@@ -608,11 +593,11 @@
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_with_token_uri(
&mut self,
- caller: caller,
- to: address,
- token_uri: string,
- ) -> Result<uint256> {
- let token_id: uint256 = <TokensMinted<T>>::get(self.id)
+ caller: Caller,
+ to: Address,
+ token_uri: String,
+ ) -> Result<U256> {
+ let token_id: U256 = <TokensMinted<T>>::get(self.id)
.checked_add(1)
.ok_or("item id overflow")?
.into();
@@ -630,10 +615,10 @@
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_with_token_uri_check_id(
&mut self,
- caller: caller,
- to: address,
- token_id: uint256,
- token_uri: string,
+ caller: Caller,
+ to: Address,
+ token_id: U256,
+ token_uri: String,
) -> Result<bool> {
let key = key::url();
let permission = get_token_permission::<T>(self.id, &key)?;
@@ -678,11 +663,6 @@
)
.map_err(dispatch_to_evm::<T>)?;
Ok(true)
- }
-
- /// @dev Not implemented
- fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
- Err("not implementable".into())
}
}
@@ -690,12 +670,12 @@
collection: &CollectionHandle<T>,
token_id: u32,
key: &up_data_structs::PropertyKey,
-) -> Result<string> {
+) -> Result<String> {
collection.consume_store_reads(1)?;
let properties = <TokenProperties<T>>::try_get((collection.id, token_id))
.map_err(|_| Error::Revert("Token properties not found".into()))?;
if let Some(property) = properties.get(key) {
- return Ok(string::from_utf8_lossy(property).into());
+ return Ok(String::from_utf8_lossy(property).into());
}
Err("Property tokenURI not found".into())
@@ -711,7 +691,7 @@
.get(key)
.map(Clone::clone)
.ok_or_else(|| {
- let key = string::from_utf8(key.clone().into_inner()).unwrap_or_default();
+ let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();
Error::Revert(alloc::format!("No permission for key {}", key))
})?;
Ok(a)
@@ -724,28 +704,28 @@
T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,
{
/// @notice A descriptive name for a collection of NFTs in this contract
- fn name(&self) -> Result<string> {
+ fn name(&self) -> Result<String> {
Ok(decode_utf16(self.name.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
/// @notice An abbreviated name for NFTs in this contract
- fn symbol(&self) -> Result<string> {
- Ok(string::from_utf8_lossy(&self.token_prefix).into())
+ fn symbol(&self) -> Result<String> {
+ Ok(String::from_utf8_lossy(&self.token_prefix).into())
}
/// @notice A description for the collection.
- fn description(&self) -> Result<string> {
+ fn description(&self) -> Result<String> {
Ok(decode_utf16(self.description.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
- fn cross_owner_of(&self, token_id: uint256) -> Result<eth::CrossAddress> {
+ fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
Self::token_owner(&self, token_id.try_into()?)
.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
.ok_or(Error::Revert("key too large".into()))
@@ -756,7 +736,7 @@
/// @param tokenId Id for the token.
/// @param keys Properties keys. Empty keys for all propertyes.
/// @return Vector of properties key/value pairs.
- fn properties(&self, token_id: uint256, keys: Vec<string>) -> Result<Vec<eth::Property>> {
+ fn properties(&self, token_id: U256, keys: Vec<String>) -> Result<Vec<eth::Property>> {
let keys = keys
.into_iter()
.map(|key| {
@@ -785,10 +765,10 @@
#[weight(<SelfWeightOf<T>>::approve())]
fn approve_cross(
&mut self,
- caller: caller,
+ caller: Caller,
approved: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let approved = approved.into_sub_cross_account::<T>()?;
let token = token_id.try_into()?;
@@ -804,7 +784,7 @@
/// @param to The new owner
/// @param tokenId The NFT to transfer
#[weight(<SelfWeightOf<T>>::transfer())]
- fn transfer(&mut self, caller: caller, to: address, token_id: uint256) -> Result<void> {
+ fn transfer(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let token = token_id.try_into()?;
@@ -824,10 +804,10 @@
#[weight(<SelfWeightOf<T>>::transfer())]
fn transfer_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
let token = token_id.try_into()?;
@@ -848,11 +828,11 @@
#[weight(<SelfWeightOf<T>>::transfer())]
fn transfer_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: eth::CrossAddress,
to: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
let to = to.into_sub_cross_account::<T>()?;
@@ -873,7 +853,7 @@
/// @param tokenId The NFT to transfer
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::burn_from())]
- fn burn_from(&mut self, caller: caller, from: address, token_id: uint256) -> Result<void> {
+ fn burn_from(&mut self, caller: Caller, from: Address, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
let token = token_id.try_into()?;
@@ -895,10 +875,10 @@
#[weight(<SelfWeightOf<T>>::burn_from())]
fn burn_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
let token = token_id.try_into()?;
@@ -912,7 +892,7 @@
}
/// @notice Returns next free NFT ID.
- fn next_token_id(&self) -> Result<uint256> {
+ fn next_token_id(&self) -> Result<U256> {
self.consume_store_reads(1)?;
Ok(<TokensMinted<T>>::get(self.id)
.checked_add(1)
@@ -927,7 +907,7 @@
/// @param tokenIds IDs of the minted NFTs
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]
- fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {
+ fn mint_bulk(&mut self, caller: Caller, to: Address, token_ids: Vec<U256>) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let mut expected_index = <TokensMinted<T>>::get(self.id)
@@ -966,9 +946,9 @@
#[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]
fn mint_bulk_with_token_uri(
&mut self,
- caller: caller,
- to: address,
- tokens: Vec<(uint256, string)>,
+ caller: Caller,
+ to: Address,
+ tokens: Vec<(U256, String)>,
) -> Result<bool> {
let key = key::url();
let caller = T::CrossAccountId::from_eth(caller);
@@ -1017,10 +997,10 @@
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: eth::CrossAddress,
properties: Vec<eth::Property>,
- ) -> Result<uint256> {
+ ) -> Result<U256> {
let token_id = <TokensMinted<T>>::get(self.id)
.checked_add(1)
.ok_or("item id overflow")?;
@@ -1055,7 +1035,7 @@
}
/// @notice Returns collection helper contract address
- fn collection_helper_address(&self) -> Result<address> {
+ fn collection_helper_address(&self) -> Result<Address> {
Ok(T::ContractAddress::get())
}
}
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -416,11 +416,9 @@
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
/// @dev EVM selector for this function is: 0x41835d4c,
/// or in textual repr: setCollectionAccess(uint8)
- function setCollectionAccess(uint8 mode) public {
+ function setCollectionAccess(AccessMode mode) public {
require(false, stub_error);
mode;
dummy = 0;
@@ -585,6 +583,14 @@
uint256 sub;
}
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList
+}
+
/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
struct CollectionNestingPermission {
CollectionPermissionField field;
@@ -700,22 +706,9 @@
}
}
-/// @dev inlined interface
-contract ERC721UniqueMintableEvents {
- event MintingFinished();
-}
-
/// @title ERC721 minting logic.
-/// @dev the ERC-165 identifier for this interface is 0x476ff149
-contract ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {
- /// @dev EVM selector for this function is: 0x05d2035b,
- /// or in textual repr: mintingFinished()
- function mintingFinished() public view returns (bool) {
- require(false, stub_error);
- dummy;
- return false;
- }
-
+/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
+contract ERC721UniqueMintable is Dummy, ERC165 {
/// @notice Function to mint a token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
@@ -756,7 +749,6 @@
dummy = 0;
return 0;
}
-
// /// @notice Function to mint token with the given tokenUri.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
@@ -774,14 +766,6 @@
// return false;
// }
- /// @dev Not implemented
- /// @dev EVM selector for this function is: 0x7d64bcb4,
- /// or in textual repr: finishMinting()
- function finishMinting() public returns (bool) {
- require(false, stub_error);
- dummy = 0;
- return false;
- }
}
/// @title Unique extensions for ERC721.
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -39,7 +39,7 @@
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::{call, dispatch_to_evm};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use sp_core::{H160, Get};
+use sp_core::{H160, U256, Get};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec, vec};
use up_data_structs::{
CollectionId, CollectionPropertiesVec, mapping::TokenAddressMapping, Property, PropertyKey,
@@ -66,8 +66,8 @@
#[solidity(hide)]
fn set_token_property_permission(
&mut self,
- caller: caller,
- key: string,
+ caller: Caller,
+ key: String,
is_mutable: bool,
collection_admin: bool,
token_owner: bool,
@@ -96,7 +96,7 @@
#[weight(<SelfWeightOf<T>>::set_token_property_permissions(permissions.len() as u32))]
fn set_token_property_permissions(
&mut self,
- caller: caller,
+ caller: Caller,
permissions: Vec<eth::TokenPropertyPermission>,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
@@ -124,10 +124,10 @@
#[weight(<SelfWeightOf<T>>::set_token_properties(1))]
fn set_property(
&mut self,
- caller: caller,
- token_id: uint256,
- key: string,
- value: bytes,
+ caller: Caller,
+ token_id: U256,
+ key: String,
+ value: Bytes,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
@@ -157,8 +157,8 @@
#[weight(<SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]
fn set_properties(
&mut self,
- caller: caller,
- token_id: uint256,
+ caller: Caller,
+ token_id: U256,
properties: Vec<eth::Property>,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
@@ -190,7 +190,7 @@
/// @param key Property key.
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::delete_token_properties(1))]
- fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {
+ fn delete_property(&mut self, token_id: U256, caller: Caller, key: String) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
let key = <Vec<u8>>::from(key)
@@ -212,9 +212,9 @@
#[weight(<SelfWeightOf<T>>::delete_token_properties(keys.len() as u32))]
fn delete_properties(
&mut self,
- token_id: uint256,
- caller: caller,
- keys: Vec<string>,
+ token_id: U256,
+ caller: Caller,
+ keys: Vec<String>,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
@@ -242,7 +242,7 @@
/// @param tokenId ID of the token.
/// @param key Property key.
/// @return Property value bytes
- fn property(&self, token_id: uint256, key: string) -> Result<bytes> {
+ fn property(&self, token_id: U256, key: String) -> Result<Bytes> {
let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
let key = <Vec<u8>>::from(key)
.try_into()
@@ -262,37 +262,30 @@
/// may be created and assigned without emitting Transfer.
Transfer {
#[indexed]
- from: address,
+ from: Address,
#[indexed]
- to: address,
+ to: Address,
#[indexed]
- token_id: uint256,
+ token_id: U256,
},
/// @dev Not supported
Approval {
#[indexed]
- owner: address,
+ owner: Address,
#[indexed]
- approved: address,
+ approved: Address,
#[indexed]
- token_id: uint256,
+ token_id: U256,
},
/// @dev Not supported
#[allow(dead_code)]
ApprovalForAll {
#[indexed]
- owner: address,
+ owner: Address,
#[indexed]
- operator: address,
+ operator: Address,
approved: bool,
},
-}
-
-#[derive(ToLog)]
-pub enum ERC721UniqueMintableEvents {
- /// @dev Not supported
- #[allow(dead_code)]
- MintingFinished {},
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
@@ -305,14 +298,14 @@
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev real implementation of this function lies in `ERC721UniqueExtensions`
#[solidity(hide, rename_selector = "name")]
- fn name_proxy(&self) -> Result<string> {
+ fn name_proxy(&self) -> Result<String> {
self.name()
}
/// @notice An abbreviated name for NFTs in this contract
/// @dev real implementation of this function lies in `ERC721UniqueExtensions`
#[solidity(hide, rename_selector = "symbol")]
- fn symbol_proxy(&self) -> Result<string> {
+ fn symbol_proxy(&self) -> Result<String> {
self.symbol()
}
@@ -326,7 +319,7 @@
///
/// @return token's const_metadata
#[solidity(rename_selector = "tokenURI")]
- fn token_uri(&self, token_id: uint256) -> Result<string> {
+ fn token_uri(&self, token_id: U256) -> Result<String> {
let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
match get_token_property(self, token_id_u32, &key::url()).as_deref() {
@@ -339,7 +332,7 @@
let base_uri =
pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())
.map(BoundedVec::into_inner)
- .map(string::from_utf8)
+ .map(String::from_utf8)
.transpose()
.map_err(|e| {
Error::Revert(alloc::format!(
@@ -372,12 +365,12 @@
/// @param index A counter less than `totalSupply()`
/// @return The token identifier for the `index`th NFT,
/// (sort order not specified)
- fn token_by_index(&self, index: uint256) -> Result<uint256> {
+ fn token_by_index(&self, index: U256) -> Result<U256> {
Ok(index)
}
/// Not implemented
- fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
+ fn token_of_owner_by_index(&self, _owner: Address, _index: U256) -> Result<U256> {
// TODO: Not implemetable
Err("not implemented".into())
}
@@ -385,7 +378,7 @@
/// @notice Count RFTs tracked by this contract
/// @return A count of valid RFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
- fn total_supply(&self) -> Result<uint256> {
+ fn total_supply(&self) -> Result<U256> {
self.consume_store_reads(1)?;
Ok(<Pallet<T>>::total_supply(self).into())
}
@@ -400,7 +393,7 @@
/// function throws for queries about the zero address.
/// @param owner An address for whom to query the balance
/// @return The number of RFTs owned by `owner`, possibly zero
- fn balance_of(&self, owner: address) -> Result<uint256> {
+ fn balance_of(&self, owner: Address) -> Result<U256> {
self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let balance = <AccountBalance<T>>::get((self.id, owner));
@@ -414,7 +407,7 @@
/// the tokens that are partially owned.
/// @param tokenId The identifier for an RFT
/// @return The address of the owner of the RFT
- fn owner_of(&self, token_id: uint256) -> Result<address> {
+ fn owner_of(&self, token_id: U256) -> Result<Address> {
self.consume_store_reads(2)?;
let token = token_id.try_into()?;
let owner = <Pallet<T>>::token_owner(self.id, token);
@@ -427,23 +420,18 @@
#[solidity(rename_selector = "safeTransferFrom")]
fn safe_transfer_from_with_data(
&mut self,
- _from: address,
- _to: address,
- _token_id: uint256,
- _data: bytes,
- ) -> Result<void> {
+ _from: Address,
+ _to: Address,
+ _token_id: U256,
+ _data: Bytes,
+ ) -> Result<()> {
// TODO: Not implemetable
Err("not implemented".into())
}
/// @dev Not implemented
#[solidity(rename_selector = "safeTransferFrom")]
- fn safe_transfer_from(
- &mut self,
- _from: address,
- _to: address,
- _token_id: uint256,
- ) -> Result<void> {
+ fn safe_transfer_from(&mut self, _from: Address, _to: Address, _token_id: U256) -> Result<()> {
// TODO: Not implemetable
Err("not implemented".into())
}
@@ -461,11 +449,11 @@
#[weight(<SelfWeightOf<T>>::transfer_from_creating_removing())]
fn transfer_from(
&mut self,
- caller: caller,
- from: address,
- to: address,
- token_id: uint256,
- ) -> Result<void> {
+ caller: Caller,
+ from: Address,
+ to: Address,
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
let to = T::CrossAccountId::from_eth(to);
@@ -484,7 +472,7 @@
}
/// @dev Not implemented
- fn approve(&mut self, _caller: caller, _approved: address, _token_id: uint256) -> Result<void> {
+ fn approve(&mut self, _caller: Caller, _approved: Address, _token_id: U256) -> Result<()> {
Err("not implemented".into())
}
@@ -495,10 +483,10 @@
#[weight(<SelfWeightOf<T>>::set_allowance_for_all())]
fn set_approval_for_all(
&mut self,
- caller: caller,
- operator: address,
+ caller: Caller,
+ operator: Address,
approved: bool,
- ) -> Result<void> {
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let operator = T::CrossAccountId::from_eth(operator);
@@ -508,14 +496,14 @@
}
/// @dev Not implemented
- fn get_approved(&self, _token_id: uint256) -> Result<address> {
+ fn get_approved(&self, _token_id: U256) -> Result<Address> {
// TODO: Not implemetable
Err("not implemented".into())
}
/// @notice Tells whether the given `owner` approves the `operator`.
#[weight(<SelfWeightOf<T>>::allowance_for_all())]
- fn is_approved_for_all(&self, owner: address, operator: address) -> Result<bool> {
+ fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {
let owner = T::CrossAccountId::from_eth(owner);
let operator = T::CrossAccountId::from_eth(operator);
@@ -563,7 +551,7 @@
/// operator of the current owner.
/// @param tokenId The RFT to approve
#[weight(<SelfWeightOf<T>>::burn_item_fully())]
- fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
+ fn burn(&mut self, caller: Caller, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let token = token_id.try_into()?;
@@ -576,18 +564,14 @@
}
/// @title ERC721 minting logic.
-#[solidity_interface(name = ERC721UniqueMintable, events(ERC721UniqueMintableEvents))]
+#[solidity_interface(name = ERC721UniqueMintable)]
impl<T: Config> RefungibleHandle<T> {
- fn minting_finished(&self) -> Result<bool> {
- Ok(false)
- }
-
/// @notice Function to mint a token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
#[weight(<SelfWeightOf<T>>::create_item())]
- fn mint(&mut self, caller: caller, to: address) -> Result<uint256> {
- let token_id: uint256 = <TokensMinted<T>>::get(self.id)
+ fn mint(&mut self, caller: Caller, to: Address) -> Result<U256> {
+ let token_id: U256 = <TokensMinted<T>>::get(self.id)
.checked_add(1)
.ok_or("item id overflow")?
.into();
@@ -602,7 +586,7 @@
/// @param tokenId ID of the minted RFT
#[solidity(hide, rename_selector = "mint")]
#[weight(<SelfWeightOf<T>>::create_item())]
- fn mint_check_id(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
+ fn mint_check_id(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let token_id: u32 = token_id.try_into()?;
@@ -645,11 +629,11 @@
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_with_token_uri(
&mut self,
- caller: caller,
- to: address,
- token_uri: string,
- ) -> Result<uint256> {
- let token_id: uint256 = <TokensMinted<T>>::get(self.id)
+ caller: Caller,
+ to: Address,
+ token_uri: String,
+ ) -> Result<U256> {
+ let token_id: U256 = <TokensMinted<T>>::get(self.id)
.checked_add(1)
.ok_or("item id overflow")?
.into();
@@ -667,10 +651,10 @@
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_with_token_uri_check_id(
&mut self,
- caller: caller,
- to: address,
- token_id: uint256,
- token_uri: string,
+ caller: Caller,
+ to: Address,
+ token_id: U256,
+ token_uri: String,
) -> Result<bool> {
let key = key::url();
let permission = get_token_permission::<T>(self.id, &key)?;
@@ -717,11 +701,6 @@
)
.map_err(dispatch_to_evm::<T>)?;
Ok(true)
- }
-
- /// @dev Not implemented
- fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
- Err("not implementable".into())
}
}
@@ -729,12 +708,12 @@
collection: &CollectionHandle<T>,
token_id: u32,
key: &up_data_structs::PropertyKey,
-) -> Result<string> {
+) -> Result<String> {
collection.consume_store_reads(1)?;
let properties = <TokenProperties<T>>::try_get((collection.id, token_id))
.map_err(|_| Error::Revert("Token properties not found".into()))?;
if let Some(property) = properties.get(key) {
- return Ok(string::from_utf8_lossy(property).into());
+ return Ok(String::from_utf8_lossy(property).into());
}
Err("Property tokenURI not found".into())
@@ -750,7 +729,7 @@
.get(key)
.map(Clone::clone)
.ok_or_else(|| {
- let key = string::from_utf8(key.clone().into_inner()).unwrap_or_default();
+ let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();
Error::Revert(alloc::format!("No permission for key {}", key))
})?;
Ok(a)
@@ -763,28 +742,28 @@
T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,
{
/// @notice A descriptive name for a collection of NFTs in this contract
- fn name(&self) -> Result<string> {
+ fn name(&self) -> Result<String> {
Ok(decode_utf16(self.name.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
/// @notice An abbreviated name for NFTs in this contract
- fn symbol(&self) -> Result<string> {
- Ok(string::from_utf8_lossy(&self.token_prefix).into())
+ fn symbol(&self) -> Result<String> {
+ Ok(String::from_utf8_lossy(&self.token_prefix).into())
}
/// @notice A description for the collection.
- fn description(&self) -> Result<string> {
+ fn description(&self) -> Result<String> {
Ok(decode_utf16(self.description.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
- fn cross_owner_of(&self, token_id: uint256) -> Result<eth::CrossAddress> {
+ fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
Self::token_owner(&self, token_id.try_into()?)
.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
.ok_or(Error::Revert("key too large".into()))
@@ -795,7 +774,7 @@
/// @param tokenId Id for the token.
/// @param keys Properties keys. Empty keys for all propertyes.
/// @return Vector of properties key/value pairs.
- fn properties(&self, token_id: uint256, keys: Vec<string>) -> Result<Vec<eth::Property>> {
+ fn properties(&self, token_id: U256, keys: Vec<String>) -> Result<Vec<eth::Property>> {
let keys = keys
.into_iter()
.map(|key| {
@@ -821,7 +800,7 @@
/// @param to The new owner
/// @param tokenId The RFT to transfer
#[weight(<SelfWeightOf<T>>::transfer_creating_removing())]
- fn transfer(&mut self, caller: caller, to: address, token_id: uint256) -> Result<void> {
+ fn transfer(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let token = token_id.try_into()?;
@@ -846,10 +825,10 @@
#[weight(<SelfWeightOf<T>>::transfer_creating_removing())]
fn transfer_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
let token = token_id.try_into()?;
@@ -874,11 +853,11 @@
#[weight(<SelfWeightOf<T>>::transfer_creating_removing())]
fn transfer_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: eth::CrossAddress,
to: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
let to = to.into_sub_cross_account::<T>()?;
@@ -904,7 +883,7 @@
/// @param tokenId The RFT to transfer
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::burn_from())]
- fn burn_from(&mut self, caller: caller, from: address, token_id: uint256) -> Result<void> {
+ fn burn_from(&mut self, caller: Caller, from: Address, token_id: U256) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
let token = token_id.try_into()?;
@@ -930,10 +909,10 @@
#[weight(<SelfWeightOf<T>>::burn_from())]
fn burn_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: eth::CrossAddress,
- token_id: uint256,
- ) -> Result<void> {
+ token_id: U256,
+ ) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
let token = token_id.try_into()?;
@@ -950,7 +929,7 @@
}
/// @notice Returns next free RFT ID.
- fn next_token_id(&self) -> Result<uint256> {
+ fn next_token_id(&self) -> Result<U256> {
self.consume_store_reads(1)?;
Ok(<TokensMinted<T>>::get(self.id)
.checked_add(1)
@@ -965,7 +944,7 @@
/// @param tokenIds IDs of the minted RFTs
#[solidity(hide)]
#[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]
- fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {
+ fn mint_bulk(&mut self, caller: Caller, to: Address, token_ids: Vec<U256>) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let mut expected_index = <TokensMinted<T>>::get(self.id)
@@ -1010,9 +989,9 @@
#[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]
fn mint_bulk_with_token_uri(
&mut self,
- caller: caller,
- to: address,
- tokens: Vec<(uint256, string)>,
+ caller: Caller,
+ to: Address,
+ tokens: Vec<(U256, String)>,
) -> Result<bool> {
let key = key::url();
let caller = T::CrossAccountId::from_eth(caller);
@@ -1067,10 +1046,10 @@
#[weight(<SelfWeightOf<T>>::create_item())]
fn mint_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: eth::CrossAddress,
properties: Vec<eth::Property>,
- ) -> Result<uint256> {
+ ) -> Result<U256> {
let token_id = <TokensMinted<T>>::get(self.id)
.checked_add(1)
.ok_or("item id overflow")?;
@@ -1109,7 +1088,7 @@
/// Returns EVM address for refungible token
///
/// @param token ID of the token
- fn token_contract_address(&self, token: uint256) -> Result<address> {
+ fn token_contract_address(&self, token: U256) -> Result<Address> {
Ok(T::EvmTokenAddressMapping::token_to_address(
self.id,
token.try_into().map_err(|_| "token id overflow")?,
@@ -1117,7 +1096,7 @@
}
/// @notice Returns collection helper contract address
- fn collection_helper_address(&self) -> Result<address> {
+ fn collection_helper_address(&self) -> Result<Address> {
Ok(T::ContractAddress::get())
}
}
pallets/refungible/src/erc_token.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -25,7 +25,8 @@
ops::Deref,
};
use evm_coder::{
- abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight,
+ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, solidity, types::*,
+ weight,
};
use pallet_common::{
CommonWeightInfo,
@@ -36,6 +37,7 @@
use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
use sp_std::vec::Vec;
+use sp_core::U256;
use up_data_structs::TokenId;
use crate::{
@@ -50,11 +52,11 @@
#[solidity_interface(name = ERC1633)]
impl<T: Config> RefungibleTokenHandle<T> {
- fn parent_token(&self) -> Result<address> {
+ fn parent_token(&self) -> Result<Address> {
Ok(collection_id_to_address(self.id))
}
- fn parent_token_id(&self) -> Result<uint256> {
+ fn parent_token_id(&self) -> Result<U256> {
Ok(self.1.into())
}
}
@@ -67,19 +69,19 @@
/// of burning tokens the transfer is to 0.
Transfer {
#[indexed]
- from: address,
+ from: Address,
#[indexed]
- to: address,
- value: uint256,
+ to: Address,
+ value: U256,
},
/// @dev This event is emitted when the amount of tokens (value) is approved
/// by the owner to be used by the spender.
Approval {
#[indexed]
- owner: address,
+ owner: Address,
#[indexed]
- spender: address,
- value: uint256,
+ spender: Address,
+ value: U256,
},
}
@@ -90,25 +92,25 @@
#[solidity_interface(name = ERC20, events(ERC20Events))]
impl<T: Config> RefungibleTokenHandle<T> {
/// @return the name of the token.
- fn name(&self) -> Result<string> {
+ fn name(&self) -> Result<String> {
Ok(decode_utf16(self.name.iter().copied())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
+ .collect::<String>())
}
/// @return the symbol of the token.
- fn symbol(&self) -> Result<string> {
- Ok(string::from_utf8_lossy(&self.token_prefix).into())
+ fn symbol(&self) -> Result<String> {
+ Ok(String::from_utf8_lossy(&self.token_prefix).into())
}
/// @dev Total number of tokens in existence
- fn total_supply(&self) -> Result<uint256> {
+ fn total_supply(&self) -> Result<U256> {
self.consume_store_reads(1)?;
Ok(<TotalSupply<T>>::get((self.id, self.1)).into())
}
/// @dev Not supported
- fn decimals(&self) -> Result<uint8> {
+ fn decimals(&self) -> Result<u8> {
// Decimals aren't supported for refungible tokens
Ok(0)
}
@@ -116,7 +118,7 @@
/// @dev Gets the balance of the specified address.
/// @param owner The address to query the balance of.
/// @return An uint256 representing the amount owned by the passed address.
- fn balance_of(&self, owner: address) -> Result<uint256> {
+ fn balance_of(&self, owner: Address) -> Result<U256> {
self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let balance = <Balance<T>>::get((self.id, self.1, owner));
@@ -127,7 +129,7 @@
/// @param to The address to transfer to.
/// @param amount The amount to be transferred.
#[weight(<CommonWeights<T>>::transfer())]
- fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
+ fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -147,10 +149,10 @@
#[weight(<CommonWeights<T>>::transfer_from())]
fn transfer_from(
&mut self,
- caller: caller,
- from: address,
- to: address,
- amount: uint256,
+ caller: Caller,
+ from: Address,
+ to: Address,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
@@ -173,7 +175,7 @@
/// @param spender The address which will spend the funds.
/// @param amount The amount of tokens to be spent.
#[weight(<SelfWeightOf<T>>::approve())]
- fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {
+ fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let spender = T::CrossAccountId::from_eth(spender);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -187,7 +189,7 @@
/// @param owner address The address which owns the funds.
/// @param spender address The address which will spend the funds.
/// @return A uint256 specifying the amount of tokens still available for the spender.
- fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
+ fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {
self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let spender = T::CrossAccountId::from_eth(spender);
@@ -206,7 +208,8 @@
/// @param from The account whose tokens will be burnt.
/// @param amount The amount that will be burnt.
#[weight(<SelfWeightOf<T>>::burn_from())]
- fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
+ #[solidity(hide)]
+ fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = T::CrossAccountId::from_eth(from);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -226,9 +229,9 @@
#[weight(<SelfWeightOf<T>>::burn_from())]
fn burn_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
@@ -252,9 +255,9 @@
#[weight(<SelfWeightOf<T>>::approve())]
fn approve_cross(
&mut self,
- caller: caller,
+ caller: Caller,
spender: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let spender = spender.into_sub_cross_account::<T>()?;
@@ -268,7 +271,7 @@
/// Throws if `msg.sender` doesn't owns all of the tokens.
/// @param amount New total amount of the tokens.
#[weight(<SelfWeightOf<T>>::repartition_item())]
- fn repartition(&mut self, caller: caller, amount: uint256) -> Result<bool> {
+ fn repartition(&mut self, caller: Caller, amount: U256) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
@@ -282,9 +285,9 @@
#[weight(<CommonWeights<T>>::transfer())]
fn transfer_cross(
&mut self,
- caller: caller,
+ caller: Caller,
to: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
@@ -305,10 +308,10 @@
#[weight(<CommonWeights<T>>::transfer_from())]
fn transfer_from_cross(
&mut self,
- caller: caller,
+ caller: Caller,
from: pallet_common::eth::CrossAddress,
to: pallet_common::eth::CrossAddress,
- amount: uint256,
+ amount: U256,
) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
let from = from.into_sub_cross_account::<T>()?;
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -416,11 +416,9 @@
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
/// @dev EVM selector for this function is: 0x41835d4c,
/// or in textual repr: setCollectionAccess(uint8)
- function setCollectionAccess(uint8 mode) public {
+ function setCollectionAccess(AccessMode mode) public {
require(false, stub_error);
mode;
dummy = 0;
@@ -585,6 +583,14 @@
uint256 sub;
}
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList
+}
+
/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
struct CollectionNestingPermission {
CollectionPermissionField field;
@@ -700,22 +706,9 @@
}
}
-/// @dev inlined interface
-contract ERC721UniqueMintableEvents {
- event MintingFinished();
-}
-
/// @title ERC721 minting logic.
-/// @dev the ERC-165 identifier for this interface is 0x476ff149
-contract ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {
- /// @dev EVM selector for this function is: 0x05d2035b,
- /// or in textual repr: mintingFinished()
- function mintingFinished() public view returns (bool) {
- require(false, stub_error);
- dummy;
- return false;
- }
-
+/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
+contract ERC721UniqueMintable is Dummy, ERC165 {
/// @notice Function to mint a token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
@@ -756,7 +749,6 @@
dummy = 0;
return 0;
}
-
// /// @notice Function to mint token with the given tokenUri.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
@@ -774,14 +766,6 @@
// return false;
// }
- /// @dev Not implemented
- /// @dev EVM selector for this function is: 0x7d64bcb4,
- /// or in textual repr: finishMinting()
- function finishMinting() public returns (bool) {
- require(false, stub_error);
- dummy = 0;
- return false;
- }
}
/// @title Unique extensions for ERC721.
pallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungibleToken.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
@@ -38,19 +38,19 @@
/// @dev the ERC-165 identifier for this interface is 0xe17a7d2b
contract ERC20UniqueExtensions is Dummy, ERC165 {
- /// @dev Function that burns an amount of the token of a given account,
- /// deducting from the sender's allowance for said account.
- /// @param from The account whose tokens will be burnt.
- /// @param amount The amount that will be burnt.
- /// @dev EVM selector for this function is: 0x79cc6790,
- /// or in textual repr: burnFrom(address,uint256)
- function burnFrom(address from, uint256 amount) public returns (bool) {
- require(false, stub_error);
- from;
- amount;
- dummy = 0;
- return false;
- }
+ // /// @dev Function that burns an amount of the token of a given account,
+ // /// deducting from the sender's allowance for said account.
+ // /// @param from The account whose tokens will be burnt.
+ // /// @param amount The amount that will be burnt.
+ // /// @dev EVM selector for this function is: 0x79cc6790,
+ // /// or in textual repr: burnFrom(address,uint256)
+ // function burnFrom(address from, uint256 amount) public returns (bool) {
+ // require(false, stub_error);
+ // from;
+ // amount;
+ // dummy = 0;
+ // return false;
+ // }
/// @dev Function that burns an amount of the token of a given account,
/// deducting from the sender's allowance for said account.
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -56,10 +56,10 @@
}
fn convert_data<T: Config>(
- caller: caller,
- name: string,
- description: string,
- token_prefix: string,
+ caller: Caller,
+ name: String,
+ description: String,
+ token_prefix: String,
) -> Result<(
T::CrossAccountId,
CollectionName,
@@ -87,13 +87,13 @@
#[inline(always)]
fn create_collection_internal<T: Config>(
- caller: caller,
- value: value,
- name: string,
+ caller: Caller,
+ value: Value,
+ name: String,
collection_mode: CollectionMode,
- description: string,
- token_prefix: string,
-) -> Result<address> {
+ description: String,
+ token_prefix: String,
+) -> Result<Address> {
let (caller, name, description, token_prefix) =
convert_data::<T>(caller, name, description, token_prefix)?;
let data = CreateCollectionData {
@@ -118,7 +118,7 @@
Ok(address)
}
-fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {
+fn check_sent_amount_equals_collection_creation_price<T: Config>(value: Value) -> Result<()> {
let value = value.as_u128();
let creation_price: u128 = T::CollectionCreationPrice::get()
.try_into()
@@ -149,12 +149,12 @@
#[solidity(rename_selector = "createNFTCollection")]
fn create_nft_collection(
&mut self,
- caller: caller,
- value: value,
- name: string,
- description: string,
- token_prefix: string,
- ) -> Result<address> {
+ caller: Caller,
+ value: Value,
+ name: String,
+ description: String,
+ token_prefix: String,
+ ) -> Result<Address> {
let (caller, name, description, token_prefix) =
convert_data::<T>(caller, name, description, token_prefix)?;
let data = CreateCollectionData {
@@ -188,12 +188,12 @@
#[solidity(hide)]
fn create_nonfungible_collection(
&mut self,
- caller: caller,
- value: value,
- name: string,
- description: string,
- token_prefix: string,
- ) -> Result<address> {
+ caller: Caller,
+ value: Value,
+ name: String,
+ description: String,
+ token_prefix: String,
+ ) -> Result<Address> {
create_collection_internal::<T>(
caller,
value,
@@ -208,12 +208,12 @@
#[solidity(rename_selector = "createRFTCollection")]
fn create_rft_collection(
&mut self,
- caller: caller,
- value: value,
- name: string,
- description: string,
- token_prefix: string,
- ) -> Result<address> {
+ caller: Caller,
+ value: Value,
+ name: String,
+ description: String,
+ token_prefix: String,
+ ) -> Result<Address> {
create_collection_internal::<T>(
caller,
value,
@@ -228,13 +228,13 @@
#[solidity(rename_selector = "createFTCollection")]
fn create_fungible_collection(
&mut self,
- caller: caller,
- value: value,
- name: string,
- decimals: uint8,
- description: string,
- token_prefix: string,
- ) -> Result<address> {
+ caller: Caller,
+ value: Value,
+ name: String,
+ decimals: u8,
+ description: String,
+ token_prefix: String,
+ ) -> Result<Address> {
create_collection_internal::<T>(
caller,
value,
@@ -248,9 +248,9 @@
#[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]
fn make_collection_metadata_compatible(
&mut self,
- caller: caller,
- collection: address,
- base_uri: string,
+ caller: Caller,
+ collection: Address,
+ base_uri: String,
) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let collection =
@@ -334,7 +334,7 @@
}
#[weight(<SelfWeightOf<T>>::destroy_collection())]
- fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {
+ fn destroy_collection(&mut self, caller: Caller, collection_address: Address) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)
@@ -346,7 +346,7 @@
/// Check if a collection exists
/// @param collectionAddress Address of the collection in question
/// @return bool Does the collection exist?
- fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {
+ fn is_collection_exist(&self, _caller: Caller, collection_address: Address) -> Result<bool> {
if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {
let collection_id = id;
return Ok(<CollectionById<T>>::contains_key(collection_id));
@@ -355,7 +355,7 @@
Ok(false)
}
- fn collection_creation_fee(&self) -> Result<value> {
+ fn collection_creation_fee(&self) -> Result<Value> {
let price: u128 = T::CollectionCreationPrice::get()
.try_into()
.map_err(|_| ()) // workaround for `expect` requiring `Debug` trait
@@ -366,14 +366,14 @@
/// Returns address of a collection.
/// @param collectionId - CollectionId of the collection
/// @return eth mirror address of the collection
- fn collection_address(&self, collection_id: uint32) -> Result<address> {
+ fn collection_address(&self, collection_id: u32) -> Result<Address> {
Ok(collection_id_to_address(collection_id.into()))
}
/// Returns collectionId of a collection.
/// @param collectionAddress - Eth address of the collection
/// @return collectionId of the collection
- fn collection_id(&self, collection_address: address) -> Result<uint32> {
+ fn collection_id(&self, collection_address: Address) -> Result<u32> {
map_eth_to_id(&collection_address)
.map(|id| id.0)
.ok_or(Error::Revert(format!(
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -266,10 +266,12 @@
/// * `token_prefix`: Byte string containing the token prefix to mark a collection
/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).
/// * `mode`: Type of items stored in the collection and type dependent data.
- // returns collection ID
+ ///
+ /// returns collection ID
+ ///
+ /// Deprecated: `create_collection_ex` is more up-to-date and advanced, prefer it instead.
#[weight = <SelfWeightOf<T>>::create_collection()]
- #[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]
- pub fn create_collection(
+ fn create_collection(
origin,
collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
runtime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -265,11 +265,9 @@
match call {
// Readonly
- ERC165Call(_, _) | MintingFinished => None,
+ ERC165Call(_, _) => None,
- // Not sponsored
- FinishMinting => None,
-
+ // Sponsored
Mint { .. }
| MintCheckId { .. }
| MintWithTokenUri { .. }
runtime/tests/src/tests.rsdiffbeforeafterboth--- a/runtime/tests/src/tests.rs
+++ b/runtime/tests/src/tests.rs
@@ -323,7 +323,7 @@
.map(|d| { d.into() })
.collect()
));
- for (index, data) in items_data.into_iter().enumerate() {
+ for (index, _data) in items_data.into_iter().enumerate() {
let balance = <pallet_refungible::Balance<Test>>::get((
CollectionId(1),
TokenId((index + 1) as u32),
tests/src/benchmarks/mintFee/benchmark.tsdiffbeforeafterboth--- a/tests/src/benchmarks/mintFee/benchmark.ts
+++ b/tests/src/benchmarks/mintFee/benchmark.ts
@@ -293,6 +293,8 @@
const evmContract = await helper.ethNativeContract.collection(
helper.ethAddress.fromCollectionId(collection.collectionId),
'nft',
+ undefined,
+ true,
);
const subTokenId = await evmContract.methods.nextTokenId().call();
@@ -351,9 +353,7 @@
encodedCall = await evmContract.methods
.setProperties(
subTokenId,
- PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
- return {field_0: p.key, field_1: p.value};
- }),
+ PROPERTIES.slice(0, setup.propertiesNumber),
)
.encodeABI();
@@ -394,9 +394,7 @@
.mintToSubstrateBulkProperty(
helper.ethAddress.fromCollectionId(collection.collectionId),
susbstrateReceiver.addressRaw,
- PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
- return {field_0: p.key, field_1: p.value};
- }),
+ PROPERTIES.slice(0, setup.propertiesNumber),
)
.send({from: ethSigner, gas: 25_000_000});
},
tests/src/benchmarks/mintFee/proxyContract.soldiffbeforeafterboth--- a/tests/src/benchmarks/mintFee/proxyContract.sol
+++ b/tests/src/benchmarks/mintFee/proxyContract.sol
@@ -3,21 +3,44 @@
import {CollectionHelpers} from "../../eth/api/CollectionHelpers.sol";
import {ContractHelpers} from "../../eth/api/ContractHelpers.sol";
import {UniqueRefungibleToken} from "../../eth/api/UniqueRefungibleToken.sol";
-import {UniqueRefungible, Collection, EthCrossAccount as RftCrossAccountId, Tuple20 as RftProperties} from "../../eth/api/UniqueRefungible.sol";
-import {UniqueNFT, EthCrossAccount as NftCrossAccountId, Tuple21 as NftProperty, TokenProperties} from "../../eth/api/UniqueNFT.sol";
+import {UniqueRefungible, Collection, CrossAddress as RftCrossAccountId, Property as RftProperty} from "../../eth/api/UniqueRefungible.sol";
+import {UniqueNFT, CrossAddress as NftCrossAccountId, Property as NftProperty} from "../../eth/api/UniqueNFT.sol";
struct Property {
string key;
bytes value;
}
+interface SoftDeprecatedMethods {
+ /// @notice Set token property value.
+ /// @dev Throws error if `msg.sender` has no permission to edit the property.
+ /// @param tokenId ID of the token.
+ /// @param key Property key.
+ /// @param value Property value.
+ /// @dev EVM selector for this function is: 0x1752d67b,
+ /// or in textual repr: setProperty(uint256,string,bytes)
+ function setProperty(
+ uint256 tokenId,
+ string memory key,
+ bytes memory value
+ ) external;
+}
+
+interface BenchUniqueRefungible is UniqueRefungible, SoftDeprecatedMethods {}
+interface BenchUniqueNFT is UniqueNFT, SoftDeprecatedMethods {}
+
+
+
contract ProxyMint {
bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible"));
bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT"));
modifier checkRestrictions(address _collection) {
Collection commonContract = Collection(_collection);
- require(commonContract.isOwnerOrAdmin(msg.sender), "Only collection admin/owner can call this method");
+ require(
+ commonContract.isOwnerOrAdminCross(RftCrossAccountId(msg.sender, 0)),
+ "Only collection admin/owner can call this method"
+ );
_;
}
@@ -58,9 +81,9 @@
function mintToSubstrateWithProperty(
address _collection,
uint256 _substrateReceiver,
- Property[] calldata properties
+ Property[] calldata _properties
) external checkRestrictions(_collection) {
- uint256 propertiesLength = properties.length;
+ uint256 propertiesLength = _properties.length;
require(propertiesLength > 0, "Properies is empty");
Collection commonContract = Collection(_collection);
@@ -68,11 +91,12 @@
uint256 tokenId;
if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
- UniqueRefungible rftCollection = UniqueRefungible(_collection);
+ BenchUniqueRefungible rftCollection = BenchUniqueRefungible(_collection);
tokenId = rftCollection.nextTokenId();
rftCollection.mint(address(this));
+
for (uint256 i = 0; i < propertiesLength; ++i) {
- rftCollection.setProperty(tokenId, properties[i].key, properties[i].value);
+ rftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value);
}
rftCollection.transferFromCross(
RftCrossAccountId(address(this), 0),
@@ -80,10 +104,10 @@
tokenId
);
} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
- UniqueNFT nftCollection = UniqueNFT(_collection);
+ BenchUniqueNFT nftCollection = BenchUniqueNFT(_collection);
tokenId = nftCollection.mint(address(this));
for (uint256 i = 0; i < propertiesLength; ++i) {
- nftCollection.setProperty(tokenId, properties[i].key, properties[i].value);
+ nftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value);
}
nftCollection.transferFromCross(
NftCrossAccountId(address(this), 0),
tests/src/check-event/burnItemEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/burnItemEvent.test.ts
+++ b/tests/src/check-event/burnItemEvent.test.ts
@@ -32,6 +32,7 @@
const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
const token = await collection.mintToken(alice, {Substrate: alice.address});
await token.burn(alice);
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/check-event/createCollectionEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/createCollectionEvent.test.ts
+++ b/tests/src/check-event/createCollectionEvent.test.ts
@@ -29,6 +29,7 @@
});
itSub('Check event from createCollection(): ', async ({helper}) => {
await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/check-event/createItemEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/createItemEvent.test.ts
+++ b/tests/src/check-event/createItemEvent.test.ts
@@ -30,6 +30,7 @@
itSub('Check event from createItem(): ', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
await collection.mintToken(alice, {Substrate: alice.address});
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/check-event/createMultipleItemsEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/createMultipleItemsEvent.test.ts
+++ b/tests/src/check-event/createMultipleItemsEvent.test.ts
@@ -35,6 +35,7 @@
{owner: {Substrate: alice.address}},
]);
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/check-event/destroyCollectionEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/destroyCollectionEvent.test.ts
+++ b/tests/src/check-event/destroyCollectionEvent.test.ts
@@ -31,6 +31,7 @@
itSub('Check event from destroyCollection(): ', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
await collection.burn(alice);
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/check-event/transferEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/transferEvent.test.ts
+++ b/tests/src/check-event/transferEvent.test.ts
@@ -34,6 +34,7 @@
const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
const token = await collection.mintToken(alice, {Substrate: alice.address});
await token.transfer(alice, {Substrate: bob.address});
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/check-event/transferFromEvent.test.tsdiffbeforeafterboth--- a/tests/src/check-event/transferFromEvent.test.ts
+++ b/tests/src/check-event/transferFromEvent.test.ts
@@ -33,6 +33,7 @@
const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
const token = await collection.mintToken(alice, {Substrate: alice.address});
await token.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address});
+ await helper.wait.newBlocks(1);
const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
const eventStrings = event.map(e => `${e.section}.${e.method}`);
tests/src/eth/abi/fungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/fungible.json
+++ b/tests/src/eth/abi/fungible.json
@@ -488,7 +488,9 @@
"type": "function"
},
{
- "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],
+ "inputs": [
+ { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
+ ],
"name": "setCollectionAccess",
"outputs": [],
"stateMutability": "nonpayable",
tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -51,12 +51,6 @@
},
{
"anonymous": false,
- "inputs": [],
- "name": "MintingFinished",
- "type": "event"
- },
- {
- "anonymous": false,
"inputs": [
{
"indexed": true,
@@ -420,13 +414,6 @@
"name": "description",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "finishMinting",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
"type": "function"
},
{
@@ -513,13 +500,6 @@
"name": "mintWithTokenURI",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "mintingFinished",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
"type": "function"
},
{
@@ -650,7 +630,9 @@
"type": "function"
},
{
- "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],
+ "inputs": [
+ { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
+ ],
"name": "setCollectionAccess",
"outputs": [],
"stateMutability": "nonpayable",
tests/src/eth/abi/reFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -51,12 +51,6 @@
},
{
"anonymous": false,
- "inputs": [],
- "name": "MintingFinished",
- "type": "event"
- },
- {
- "anonymous": false,
"inputs": [
{
"indexed": true,
@@ -402,13 +396,6 @@
"name": "description",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "finishMinting",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
"type": "function"
},
{
@@ -495,13 +482,6 @@
"name": "mintWithTokenURI",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "mintingFinished",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
"type": "function"
},
{
@@ -632,7 +612,9 @@
"type": "function"
},
{
- "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],
+ "inputs": [
+ { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
+ ],
"name": "setCollectionAccess",
"outputs": [],
"stateMutability": "nonpayable",
tests/src/eth/abi/reFungibleToken.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungibleToken.json
+++ b/tests/src/eth/abi/reFungibleToken.json
@@ -98,16 +98,6 @@
},
{
"inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "burnFrom",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
{
"components": [
{ "internalType": "address", "name": "eth", "type": "address" },
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -175,11 +175,9 @@
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
/// @dev EVM selector for this function is: 0x41835d4c,
/// or in textual repr: setCollectionAccess(uint8)
- function setCollectionAccess(uint8 mode) external;
+ function setCollectionAccess(AccessMode mode) external;
/// Checks that user allowed to operate with collection.
///
@@ -285,6 +283,14 @@
uint256 sub;
}
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList
+}
+
/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
struct CollectionNestingPermission {
CollectionPermissionField field;
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -275,11 +275,9 @@
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
/// @dev EVM selector for this function is: 0x41835d4c,
/// or in textual repr: setCollectionAccess(uint8)
- function setCollectionAccess(uint8 mode) external;
+ function setCollectionAccess(AccessMode mode) external;
/// Checks that user allowed to operate with collection.
///
@@ -385,6 +383,14 @@
uint256 sub;
}
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList
+}
+
/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
struct CollectionNestingPermission {
CollectionPermissionField field;
@@ -481,20 +487,11 @@
/// @dev EVM selector for this function is: 0x42966c68,
/// or in textual repr: burn(uint256)
function burn(uint256 tokenId) external;
-}
-
-/// @dev inlined interface
-interface ERC721UniqueMintableEvents {
- event MintingFinished();
}
/// @title ERC721 minting logic.
-/// @dev the ERC-165 identifier for this interface is 0x476ff149
-interface ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {
- /// @dev EVM selector for this function is: 0x05d2035b,
- /// or in textual repr: mintingFinished()
- function mintingFinished() external view returns (bool);
-
+/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
+interface ERC721UniqueMintable is Dummy, ERC165 {
/// @notice Function to mint a token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
@@ -518,7 +515,6 @@
/// @dev EVM selector for this function is: 0x45c17782,
/// or in textual repr: mintWithTokenURI(address,string)
function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);
-
// /// @notice Function to mint token with the given tokenUri.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
@@ -529,10 +525,6 @@
// /// or in textual repr: mintWithTokenURI(address,uint256,string)
// function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);
- /// @dev Not implemented
- /// @dev EVM selector for this function is: 0x7d64bcb4,
- /// or in textual repr: finishMinting()
- function finishMinting() external returns (bool);
}
/// @title Unique extensions for ERC721.
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -275,11 +275,9 @@
/// Set the collection access method.
/// @param mode Access mode
- /// 0 for Normal
- /// 1 for AllowList
/// @dev EVM selector for this function is: 0x41835d4c,
/// or in textual repr: setCollectionAccess(uint8)
- function setCollectionAccess(uint8 mode) external;
+ function setCollectionAccess(AccessMode mode) external;
/// Checks that user allowed to operate with collection.
///
@@ -385,6 +383,14 @@
uint256 sub;
}
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+ /// Access grant for owner and admins. Used as default.
+ Normal,
+ /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+ AllowList
+}
+
/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
struct CollectionNestingPermission {
CollectionPermissionField field;
@@ -481,20 +487,11 @@
/// @dev EVM selector for this function is: 0x42966c68,
/// or in textual repr: burn(uint256)
function burn(uint256 tokenId) external;
-}
-
-/// @dev inlined interface
-interface ERC721UniqueMintableEvents {
- event MintingFinished();
}
/// @title ERC721 minting logic.
-/// @dev the ERC-165 identifier for this interface is 0x476ff149
-interface ERC721UniqueMintable is Dummy, ERC165, ERC721UniqueMintableEvents {
- /// @dev EVM selector for this function is: 0x05d2035b,
- /// or in textual repr: mintingFinished()
- function mintingFinished() external view returns (bool);
-
+/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
+interface ERC721UniqueMintable is Dummy, ERC165 {
/// @notice Function to mint a token.
/// @param to The new owner
/// @return uint256 The id of the newly minted token
@@ -518,7 +515,6 @@
/// @dev EVM selector for this function is: 0x45c17782,
/// or in textual repr: mintWithTokenURI(address,string)
function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);
-
// /// @notice Function to mint token with the given tokenUri.
// /// @dev `tokenId` should be obtained with `nextTokenId` method,
// /// unlike standard, you can't specify it manually
@@ -529,10 +525,6 @@
// /// or in textual repr: mintWithTokenURI(address,uint256,string)
// function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);
- /// @dev Not implemented
- /// @dev EVM selector for this function is: 0x7d64bcb4,
- /// or in textual repr: finishMinting()
- function finishMinting() external returns (bool);
}
/// @title Unique extensions for ERC721.
tests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungibleToken.sol
+++ b/tests/src/eth/api/UniqueRefungibleToken.sol
@@ -25,13 +25,13 @@
/// @dev the ERC-165 identifier for this interface is 0xe17a7d2b
interface ERC20UniqueExtensions is Dummy, ERC165 {
- /// @dev Function that burns an amount of the token of a given account,
- /// deducting from the sender's allowance for said account.
- /// @param from The account whose tokens will be burnt.
- /// @param amount The amount that will be burnt.
- /// @dev EVM selector for this function is: 0x79cc6790,
- /// or in textual repr: burnFrom(address,uint256)
- function burnFrom(address from, uint256 amount) external returns (bool);
+ // /// @dev Function that burns an amount of the token of a given account,
+ // /// deducting from the sender's allowance for said account.
+ // /// @param from The account whose tokens will be burnt.
+ // /// @param amount The amount that will be burnt.
+ // /// @dev EVM selector for this function is: 0x79cc6790,
+ // /// or in textual repr: burnFrom(address,uint256)
+ // function burnFrom(address from, uint256 amount) external returns (bool);
/// @dev Function that burns an amount of the token of a given account,
/// deducting from the sender's allowance for said account.
tests/src/eth/base.test.tsdiffbeforeafterboth--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -108,10 +108,6 @@
await checkInterface(helper, '0x5b5e139f', false, true);
});
- itEth('ERC721UniqueMintable - 0x476ff149 - support', async ({helper}) => {
- await checkInterface(helper, '0x476ff149', true, true);
- });
-
itEth('ERC721Enumerable - 0x780e9d63 - support', async ({helper}) => {
await checkInterface(helper, '0x780e9d63', true, true);
});