git.delta.rocks / unique-network / refs/commits / 8e4698bc6eb3

difftreelog

Merge pull request #160 from UniqueNetwork/feature/erc-mintable-burnable

kozyrevdev2021-08-30parents: #da8dccd #ac8c84f.patch.diff
in: master
Implement ERC721Burnable/ERC721Mintable

13 files changed

modifiedCargo.lockdiffbeforeafterboth
5849 "sp-std",5849 "sp-std",
5850]5850]
5851
5852[[package]]
5853name = "pallet-scheduler"
5854version = "3.0.0"
5855dependencies = [
5856 "frame-benchmarking",
5857 "frame-support",
5858 "frame-system",
5859 "log",
5860 "parity-scale-codec",
5861 "serde",
5862 "sp-core",
5863 "sp-io",
5864 "sp-runtime",
5865 "sp-std",
5866 "substrate-test-utils",
5867 "up-sponsorship",
5868]
58695851
5870[[package]]5852[[package]]
5871name = "pallet-scheduler"5853name = "pallet-scheduler"
5882 "sp-std",5864 "sp-std",
5883]5865]
5866
5867[[package]]
5868name = "pallet-scheduler"
5869version = "3.0.0"
5870dependencies = [
5871 "frame-benchmarking",
5872 "frame-support",
5873 "frame-system",
5874 "log",
5875 "parity-scale-codec",
5876 "serde",
5877 "sp-core",
5878 "sp-io",
5879 "sp-runtime",
5880 "sp-std",
5881 "substrate-test-utils",
5882 "up-sponsorship",
5883]
58845884
5885[[package]]5885[[package]]
5886name = "pallet-session"5886name = "pallet-session"
10692 "syn",10692 "syn",
10693]10693]
10694
10695[[package]]
10696name = "substrate-wasm-builder"
10697version = "4.0.0"
10698source = "registry+https://github.com/rust-lang/crates.io-index"
10699checksum = "93a3d51ad6abbc408b03ea962062bfcc959b438a318d7d4bedd181e1effd0610"
10700dependencies = [
10701 "ansi_term 0.12.1",
10702 "atty",
10703 "build-helper",
10704 "cargo_metadata 0.12.3",
10705 "tempfile",
10706 "toml",
10707 "walkdir",
10708 "wasm-gc-api",
10709]
1071010694
10711[[package]]10695[[package]]
10712name = "substrate-wasm-builder"10696name = "substrate-wasm-builder"
10724 "wasm-gc-api",10708 "wasm-gc-api",
10725]10709]
10710
10711[[package]]
10712name = "substrate-wasm-builder"
10713version = "4.0.0"
10714source = "registry+https://github.com/rust-lang/crates.io-index"
10715checksum = "93a3d51ad6abbc408b03ea962062bfcc959b438a318d7d4bedd181e1effd0610"
10716dependencies = [
10717 "ansi_term 0.12.1",
10718 "atty",
10719 "build-helper",
10720 "cargo_metadata 0.12.3",
10721 "tempfile",
10722 "toml",
10723 "walkdir",
10724 "wasm-gc-api",
10725]
1072610726
10727[[package]]10727[[package]]
10728name = "subtle"10728name = "subtle"
modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
74 }74 }
75 }75 }
76
77 fn expand_generator(&self) -> proc_macro2::TokenStream {
78 let pascal_call_name = &self.pascal_call_name;
79 quote! {
80 #pascal_call_name::generate_solidity_interface(out_set, is_impl);
81 }
82 }
83
84 fn expand_event_generator(&self) -> proc_macro2::TokenStream {
85 let name = &self.name;
86 quote! {
87 #name::generate_solidity_interface(out_set, is_impl);
88 }
89 }
76}90}
7791
78#[derive(Default)]92#[derive(Default)]
109123
110struct MethodArg {124struct MethodArg {
111 name: Ident,125 name: Ident,
126 camel_name: String,
112 ty: Ident,127 ty: Ident,
113}128}
114impl MethodArg {129impl MethodArg {
115 fn try_from(value: &PatType) -> syn::Result<Self> {130 fn try_from(value: &PatType) -> syn::Result<Self> {
116 Ok(Self {
117 name: parse_ident_from_pat(&value.pat)?.clone(),131 let name = parse_ident_from_pat(&value.pat)?.clone();
132 Ok(Self {
133 camel_name: cases::camelcase::to_camel_case(&name.to_string()),
134 name,
118 ty: parse_ident_from_type(&value.ty, false)?.clone(),135 ty: parse_ident_from_type(&value.ty, false)?.clone(),
119 })136 })
120 }137 }
121 fn is_value(&self) -> bool {138 fn is_value(&self) -> bool {
122 self.ty == "value"139 self.ty == "value"
168 }185 }
169186
170 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {187 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
171 let name = &self.name.to_string();188 let camel_name = &self.camel_name.to_string();
172 let ty = &self.ty;189 let ty = &self.ty;
173 quote! {190 quote! {
174 <NamedArgument<#ty>>::new(#name)191 <NamedArgument<#ty>>::new(#camel_name)
175 }192 }
176 }193 }
177}194}
400 let args = self.args.iter().map(MethodArg::expand_solidity_argument);417 let args = self
418 .args
419 .iter()
420 .filter(|a| !a.is_special())
421 .map(MethodArg::expand_solidity_argument);
401422
402 quote! {423 quote! {
475 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);496 let call_variants_this = self.methods.iter().map(Method::expand_variant_call);
476 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);497 let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);
498
499 // TODO: Inline inline_is
500 let solidity_is = self
501 .info
502 .is
503 .0
504 .iter()
505 .chain(self.info.inline_is.0.iter())
506 .map(|is| is.name.to_string());
507 let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());
508 let solidity_generators = self
509 .info
510 .is
511 .0
512 .iter()
513 .chain(self.info.inline_is.0.iter())
514 .map(Is::expand_generator);
515 let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);
477516
478 // let methods = self.methods.iter().map(Method::solidity_def);517 // let methods = self.methods.iter().map(Method::solidity_def);
479518
505 )*544 )*
506 )545 )
507 }546 }
508 pub fn generate_solidity_interface() -> string {547 pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {
509 use evm_coder::solidity::*;548 use evm_coder::solidity::*;
510 use core::fmt::Write;549 use core::fmt::Write;
511 let interface = SolidityInterface {550 let interface = SolidityInterface {
512 name: #solidity_name,551 name: #solidity_name,
552 is: &["Dummy", #(
553 #solidity_is,
554 )* #(
555 #solidity_events_is,
556 )* ],
513 functions: (#(557 functions: (#(
514 #solidity_functions,558 #solidity_functions,
515 )*),559 )*),
516 };560 };
561 if is_impl {
562 out_set.insert("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\n".into());
563 } else {
564 out_set.insert("// Common stubs holder\ninterface Dummy {\n}\n".into());
565 }
566 #(
567 #solidity_generators
568 )*
569 #(
570 #solidity_event_generators
571 )*
572
517 let mut out = string::new();573 let mut out = string::new();
574 // In solidity interface usage (is) should be preceeded by interface definition
575 // This comment helps to sort it in a set
576 if #solidity_name.starts_with("Inline") {
577 out.push_str("// Inline\n");
578 }
518 let _ = interface.format(&mut out);579 let _ = interface.format(is_impl, &mut out);
519 out580 out_set.insert(out);
520 }581 }
521 }582 }
522 impl ::evm_coder::Call for #call_name {583 impl ::evm_coder::Call for #call_name {
modifiedcrates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth
1use inflector::cases;
1use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};2use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};
2use std::fmt::Write;3use std::fmt::Write;
3use quote::quote;4use quote::quote;
67
7struct EventField {8struct EventField {
8 name: Ident,9 name: Ident,
10 camel_name: String,
9 ty: Ident,11 ty: Ident,
10 indexed: bool,12 indexed: bool,
11}13}
24 }26 }
25 Ok(Self {27 Ok(Self {
26 name: name.to_owned(),28 name: name.to_owned(),
29 camel_name: cases::camelcase::to_camel_case(&name.to_string()),
27 ty: ty.to_owned(),30 ty: ty.to_owned(),
28 indexed,31 indexed,
29 })32 })
30 }33 }
34 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
35 let camel_name = &self.camel_name;
36 let ty = &self.ty;
37 quote! {
38 <NamedArgument<#ty>>::new(#camel_name)
39 }
40 }
31}41}
3242
33struct Event {43struct Event {
117 }127 }
118 }128 }
129
130 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
131 let name = self.name.to_string();
132 let args = self.fields.iter().map(EventField::expand_solidity_argument);
133 quote! {
134 SolidityEvent {
135 name: #name,
136 args: (
137 #(
138 #args,
139 )*
140 ),
141 }
142 }
143 }
119}144}
120145
121pub struct Events {146pub struct Events {
144169
145 let consts = self.events.iter().map(Event::expand_consts);170 let consts = self.events.iter().map(Event::expand_consts);
146 let serializers = self.events.iter().map(Event::expand_serializers);171 let serializers = self.events.iter().map(Event::expand_serializers);
172 let solidity_name = self.name.to_string();
173 let solidity_functions = self.events.iter().map(Event::expand_solidity_function);
147174
148 quote! {175 quote! {
149 impl #name {176 impl #name {
150 #(177 #(
151 #consts178 #consts
152 )*179 )*
180
181 pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {
182 use evm_coder::solidity::*;
183 use core::fmt::Write;
184 let interface = SolidityInterface {
185 name: #solidity_name,
186 is: &[],
187 functions: (#(
188 #solidity_functions,
189 )*),
190 };
191 let mut out = string::new();
192 out.push_str("// Inline\n");
193 let _ = interface.format(is_impl, &mut out);
194 out_set.insert(out);
195 }
153 }196 }
154197
155 #[automatically_derived]198 #[automatically_derived]
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
19#[derive(Clone)]19#[derive(Clone)]
20pub struct AbiReader<'i> {20pub struct AbiReader<'i> {
21 buf: &'i [u8],21 buf: &'i [u8],
22 subresult_offset: usize,
22 offset: usize,23 offset: usize,
23}24}
24impl<'i> AbiReader<'i> {25impl<'i> AbiReader<'i> {
25 pub fn new(buf: &'i [u8]) -> Self {26 pub fn new(buf: &'i [u8]) -> Self {
26 Self { buf, offset: 0 }27 Self {
28 buf,
29 subresult_offset: 0,
30 offset: 0,
31 }
27 }32 }
28 pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> {33 pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> {
41 u32::from_be_bytes(method_id),
42 Self {
43 buf,
44 subresult_offset: 4,
45 offset: 4,
46 },
47 ))
36 }48 }
3749
38 fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {50 fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {
39 if self.buf.len() - self.offset < 32 {51 if self.buf.len() - self.offset < ABI_ALIGNMENT {
40 return Err(Error::Error(ExitError::OutOfOffset));52 return Err(Error::Error(ExitError::OutOfOffset));
41 }53 }
42 let mut block = [0; S];54 let mut block = [0; S];
79 }91 }
80 Ok(subresult.buf[subresult.offset..subresult.offset + length].into())92 Ok(subresult.buf[subresult.offset..subresult.offset + length].into())
81 }93 }
94 pub fn string(&mut self) -> Result<string> {
95 string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
96 }
8297
83 pub fn uint32(&mut self) -> Result<u32> {98 pub fn uint32(&mut self) -> Result<u32> {
84 Ok(u32::from_be_bytes(self.read_padleft()?))99 Ok(u32::from_be_bytes(self.read_padleft()?))
103118
104 fn subresult(&mut self) -> Result<AbiReader<'i>> {119 fn subresult(&mut self) -> Result<AbiReader<'i>> {
105 let offset = self.read_usize()?;120 let offset = self.read_usize()?;
121 if offset + self.subresult_offset > self.buf.len() {
122 return Err(Error::Error(ExitError::InvalidRange));
123 }
106 Ok(AbiReader {124 Ok(AbiReader {
107 buf: self.buf,125 buf: self.buf,
126 subresult_offset: offset + self.subresult_offset,
108 offset: offset + self.offset,127 offset: offset + self.subresult_offset,
109 })128 })
110 }129 }
111130
196 for (static_offset, part) in self.dynamic_part {215 for (static_offset, part) in self.dynamic_part {
197 let part_offset = self.static_part.len();216 let part_offset = self.static_part.len();
198217
199 let encoded_dynamic_offset = usize::to_be_bytes(part_offset - static_offset);218 let encoded_dynamic_offset = usize::to_be_bytes(part_offset);
200 self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()219 self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()
201 ..static_offset + ABI_ALIGNMENT]220 ..static_offset + ABI_ALIGNMENT]
202 .copy_from_slice(&encoded_dynamic_offset);221 .copy_from_slice(&encoded_dynamic_offset);
226impl_abi_readable!(H160, address);245impl_abi_readable!(H160, address);
227impl_abi_readable!(Vec<u8>, bytes);246impl_abi_readable!(Vec<u8>, bytes);
228impl_abi_readable!(bool, bool);247impl_abi_readable!(bool, bool);
248impl_abi_readable!(string, string);
229249
230pub trait AbiWrite {250pub trait AbiWrite {
231 fn abi_write(&self, writer: &mut AbiWriter);251 fn abi_write(&self, writer: &mut AbiWriter);
286 }}306 }}
287}307}
308
309#[cfg(test)]
310pub mod test {
311 use super::{AbiReader, AbiWriter};
312 use hex_literal::hex;
313
314 #[test]
315 fn dynamic_after_static() {
316 let mut encoder = AbiWriter::new();
317 encoder.bool(&true);
318 encoder.string("test");
319 let encoded = encoder.finish();
320
321 let mut encoder = AbiWriter::new();
322 encoder.bool(&true);
323 // Offset to subresult
324 encoder.uint32(&(32 * 2));
325 // Len of "test"
326 encoder.uint32(&4);
327 encoder.write_padright(&[b't', b'e', b's', b't']);
328 let alternative_encoded = encoder.finish();
329
330 assert_eq!(encoded, alternative_encoded);
331
332 let mut decoder = AbiReader::new(&encoded);
333 assert_eq!(decoder.bool().unwrap(), true);
334 assert_eq!(decoder.string().unwrap(), "test");
335 }
336
337 #[test]
338 fn mint_sample() {
339 let (call, mut decoder) = AbiReader::new_call(&hex!(
340 "
341 50bb4e7f
342 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374
343 0000000000000000000000000000000000000000000000000000000000000001
344 0000000000000000000000000000000000000000000000000000000000000060
345 0000000000000000000000000000000000000000000000000000000000000008
346 5465737420555249000000000000000000000000000000000000000000000000
347 "
348 ))
349 .unwrap();
350 assert_eq!(call, 0x50bb4e7f);
351 assert_eq!(
352 format!("{:?}", decoder.address().unwrap()),
353 "0xad2c0954693c2b5404b7e50967d3481bea432374"
354 );
355 assert_eq!(decoder.uint32().unwrap(), 1);
356 assert_eq!(decoder.string().unwrap(), "Test URI");
357 }
358}
288359
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
66
7pub trait SolidityTypeName: 'static {7pub trait SolidityTypeName: 'static {
8 fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result;8 fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result;
9 fn solidity_default(writer: &mut impl fmt::Write) -> fmt::Result;
9 fn is_void() -> bool {10 fn is_void() -> bool {
10 false11 false
11 }12 }
12}13}
1314
14macro_rules! solidity_type_name {15macro_rules! solidity_type_name {
15 ($($ty:ident => $name:expr),* $(,)?) => {16 ($($ty:ident => $name:literal = $default:literal),* $(,)?) => {
16 $(17 $(
17 impl SolidityTypeName for $ty {18 impl SolidityTypeName for $ty {
18 fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {19 fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
19 write!(writer, $name)20 write!(writer, $name)
20 }21 }
22 fn solidity_default(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
23 write!(writer, $default)
24 }
21 }25 }
22 )*26 )*
23 };27 };
24}28}
2529
26solidity_type_name! {30solidity_type_name! {
27 uint8 => "uint8",31 uint8 => "uint8" = "0",
28 uint32 => "uint32",32 uint32 => "uint32" = "0",
29 uint128 => "uint128",33 uint128 => "uint128" = "0",
30 uint256 => "uint256",34 uint256 => "uint256" = "0",
31 address => "address",35 address => "address" = "0x0000000000000000000000000000000000000000",
32 string => "memory string",36 string => "string memory" = "\"\"",
33 bytes => "memory bytes",37 bytes => "bytes memory" = "hex\"\"",
34 bool => "bool",38 bool => "bool" = "false",
35}39}
36impl SolidityTypeName for void {40impl SolidityTypeName for void {
37 fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {41 fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {
38 Ok(())42 Ok(())
39 }43 }
44 fn solidity_default(_writer: &mut impl fmt::Write) -> fmt::Result {
45 Ok(())
46 }
40 fn is_void() -> bool {47 fn is_void() -> bool {
41 true48 true
42 }49 }
43}50}
4451
45pub trait SolidityArguments {52pub trait SolidityArguments {
46 fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;53 fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
54 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;
55 fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result;
47 fn is_empty(&self) -> bool {56 fn is_empty(&self) -> bool {
48 self.len() == 057 self.len() == 0
49 }58 }
61 Ok(())70 Ok(())
62 }71 }
63 }72 }
73 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
74 Ok(())
75 }
76 fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
77 T::solidity_default(writer)
78 }
64 fn len(&self) -> usize {79 fn len(&self) -> usize {
65 if T::is_void() {80 if T::is_void() {
66 081 0
87 Ok(())102 Ok(())
88 }103 }
89 }104 }
105 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
106 writeln!(writer, "\t\t{};", self.0)
107 }
108 fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
109 T::solidity_default(writer)
110 }
90 fn len(&self) -> usize {111 fn len(&self) -> usize {
91 if T::is_void() {112 if T::is_void() {
92 0113 0
100 fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {121 fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
101 Ok(())122 Ok(())
102 }123 }
124 fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
125 Ok(())
126 }
127 fn solidity_default(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
128 Ok(())
129 }
103 fn len(&self) -> usize {130 fn len(&self) -> usize {
104 0131 0
105 }132 }
122 )* );149 )* );
123 Ok(())150 Ok(())
124 }151 }
152 fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
153 for_tuples!( #(
154 Tuple.solidity_get(writer)?;
155 )* );
156 Ok(())
157 }
158 fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
159 if self.is_empty() {
160 Ok(())
161 } else if self.len() == 1 {
162 for_tuples!( #(
163 Tuple.solidity_default(writer)?;
164 )* );
165 Ok(())
166 } else {
167 write!(writer, "(")?;
168 let mut first = true;
169 for_tuples!( #(
170 if !Tuple.is_empty() {
171 if !first {
172 write!(writer, ", ")?;
173 }
174 first = false;
175 Tuple.solidity_name(writer)?;
176 }
177 )* );
178 write!(writer, ")")?;
179 Ok(())
180 }
181 }
125 fn len(&self) -> usize {182 fn len(&self) -> usize {
126 for_tuples!( #( Tuple.len() )+* )183 for_tuples!( #( Tuple.len() )+* )
127 }184 }
128}185}
129186
130pub trait SolidityFunctions {187pub trait SolidityFunctions {
131 fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;188 fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result;
132}189}
133190
134pub enum SolidityMutability {191pub enum SolidityMutability {
143 pub mutability: SolidityMutability,200 pub mutability: SolidityMutability,
144}201}
145impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {202impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
146 fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {203 fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
147 write!(writer, "function {}(", self.name)?;204 write!(writer, "\tfunction {}(", self.name)?;
148 self.args.solidity_name(writer)?;205 self.args.solidity_name(writer)?;
149 write!(writer, ") external")?;206 write!(writer, ")")?;
207 if is_impl {
208 write!(writer, " public")?;
209 } else {
210 write!(writer, " external")?;
211 }
150 match &self.mutability {212 match &self.mutability {
151 SolidityMutability::Pure => write!(writer, " pure")?,213 SolidityMutability::Pure => write!(writer, " pure")?,
152 SolidityMutability::View => write!(writer, " view")?,214 SolidityMutability::View => write!(writer, " view")?,
157 self.result.solidity_name(writer)?;219 self.result.solidity_name(writer)?;
158 write!(writer, ")")?;220 write!(writer, ")")?;
159 }221 }
222 if is_impl {
223 writeln!(writer, " {{")?;
224 writeln!(writer, "\t\trequire(false, stub_error);")?;
225 self.args.solidity_get(writer)?;
226 match &self.mutability {
227 SolidityMutability::Pure => {}
228 SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,
229 SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,
230 }
231 if !self.result.is_empty() {
232 write!(writer, "\t\treturn ")?;
233 self.result.solidity_default(writer)?;
234 writeln!(writer, ";")?;
235 }
236 writeln!(writer, "\t}}")?;
237 } else {
160 writeln!(writer, ";")238 writeln!(writer, ";")?;
239 }
240 Ok(())
161 }241 }
162}242}
163243
164#[impl_for_tuples(0, 12)]244#[impl_for_tuples(0, 12)]
165impl SolidityFunctions for Tuple {245impl SolidityFunctions for Tuple {
166 for_tuples!( where #( Tuple: SolidityFunctions ),* );246 for_tuples!( where #( Tuple: SolidityFunctions ),* );
167247
168 fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {248 fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
169 let mut first = false;249 let mut first = false;
170 for_tuples!( #(250 for_tuples!( #(
171 Tuple.solidity_name(writer)?;251 Tuple.solidity_name(is_impl, writer)?;
172 )* );252 )* );
173 Ok(())253 Ok(())
174 }254 }
175}255}
176256
177pub struct SolidityInterface<F: SolidityFunctions> {257pub struct SolidityInterface<F: SolidityFunctions> {
178 pub name: &'static str,258 pub name: &'static str,
259 pub is: &'static [&'static str],
179 pub functions: F,260 pub functions: F,
180}261}
181262
182impl<F: SolidityFunctions> SolidityInterface<F> {263impl<F: SolidityFunctions> SolidityInterface<F> {
183 pub fn format(&self, out: &mut impl fmt::Write) -> fmt::Result {264 pub fn format(&self, is_impl: bool, out: &mut impl fmt::Write) -> fmt::Result {
265 if is_impl {
266 write!(out, "contract ")?;
267 } else {
268 write!(out, "interface ")?;
269 }
184 writeln!(out, "interface {} {{", self.name)?;270 write!(out, "{}", self.name)?;
271 if !self.is.is_empty() {
272 write!(out, " is")?;
273 for (i, n) in self.is.iter().enumerate() {
274 if i != 0 {
275 write!(out, ",")?;
276 }
277 write!(out, " {}", n)?;
278 }
279 }
280 writeln!(out, " {{")?;
185 self.functions.solidity_name(out)?;281 self.functions.solidity_name(is_impl, out)?;
186 writeln!(out, "}}")?;282 writeln!(out, "}}")?;
187 Ok(())283 Ok(())
188 }284 }
189}285}
286
287pub struct SolidityEvent<A> {
288 pub name: &'static str,
289 pub args: A,
290}
291
292impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
293 fn solidity_name(&self, _is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
294 write!(writer, "\tevent {}(", self.name)?;
295 self.args.solidity_name(writer)?;
296 writeln!(writer, ");")
297 }
298}
190299
modifiedpallets/nft/src/eth/erc.rsdiffbeforeafterboth
1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};
3use nft_data_structs::{CreateItemData, CreateNftData};
3use core::convert::TryInto;4use core::convert::TryInto;
4use alloc::format;
5use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};5use crate::{
6 Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,
7 ItemListIndex,
8};
6use frame_support::storage::StorageDoubleMap;9use frame_support::storage::{StorageMap, StorageDoubleMap};
7use pallet_evm::AddressMapping;10use pallet_evm::AddressMapping;
8use super::account::CrossAccountId;11use super::account::CrossAccountId;
9use sp_std::vec::Vec;12use sp_std::{vec, vec::Vec};
1013
11#[solidity_interface(name = "ERC165")]14#[solidity_interface(name = "ERC165")]
12impl<T: Config> CollectionHandle<T> {15impl<T: Config> CollectionHandle<T> {
4245
43#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]46#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]
44impl<T: Config> CollectionHandle<T> {47impl<T: Config> CollectionHandle<T> {
48 #[solidity(rename_selector = "tokenURI")]
45 fn token_uri(&self, token_id: uint256) -> Result<string> {49 fn token_uri(&self, token_id: uint256) -> Result<string> {
46 // TODO: We should standartize url prefix, maybe via offchain schema?50 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
47 Ok(format!("unique.network/{}/{}", self.id, token_id))51 Ok(string::from_utf8_lossy(
52 &<NftItemList<T>>::get(self.id, token_id)
53 .ok_or("token not found")?
54 .const_data,
55 )
56 .into())
48 }57 }
49}58}
5059
178 }187 }
179}188}
189
190#[solidity_interface(name = "ERC721Burnable")]
191impl<T: Config> CollectionHandle<T> {
192 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
193 let caller = T::CrossAccountId::from_eth(caller);
194 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
195
196 <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;
197 Ok(())
198 }
199}
200
201#[derive(ToLog)]
202pub enum ERC721MintableEvents {
203 #[allow(dead_code)]
204 MintingFinished {},
205}
206
207#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
208impl<T: Config> CollectionHandle<T> {
209 fn minting_finished(&self) -> Result<bool> {
210 Ok(false)
211 }
212
213 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
214 let caller = T::CrossAccountId::from_eth(caller);
215 let to = T::CrossAccountId::from_eth(to);
216 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
217 if <ItemListIndex>::get(self.id)
218 .checked_add(1)
219 .ok_or("item id overflow")?
220 != token_id
221 {
222 return Err("item id should be next".into());
223 }
224
225 <Module<T>>::create_item_internal(
226 &caller,
227 &self,
228 &to,
229 CreateItemData::NFT(CreateNftData {
230 const_data: vec![].try_into().unwrap(),
231 variable_data: vec![].try_into().unwrap(),
232 }),
233 )
234 .map_err(|_| "mint error")?;
235 Ok(true)
236 }
237
238 #[solidity(rename_selector = "mintWithTokenURI")]
239 fn mint_with_token_uri(
240 &mut self,
241 caller: caller,
242 to: address,
243 token_id: uint256,
244 token_uri: string,
245 ) -> Result<bool> {
246 let caller = T::CrossAccountId::from_eth(caller);
247 let to = T::CrossAccountId::from_eth(to);
248 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
249 if <ItemListIndex>::get(self.id)
250 .checked_add(1)
251 .ok_or("item id overflow")?
252 != token_id
253 {
254 return Err("item id should be next".into());
255 }
256
257 <Module<T>>::create_item_internal(
258 &caller,
259 &self,
260 &to,
261 CreateItemData::NFT(CreateNftData {
262 const_data: Vec::<u8>::from(token_uri)
263 .try_into()
264 .map_err(|_| "token uri is too long")?,
265 variable_data: vec![].try_into().unwrap(),
266 }),
267 )
268 .map_err(|_| "mint error")?;
269 Ok(true)
270 }
271
272 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
273 Err("not implementable".into())
274 }
275}
180276
181#[solidity_interface(name = "ERC721UniqueExtensions")]277#[solidity_interface(name = "ERC721UniqueExtensions")]
182impl<T: Config> CollectionHandle<T> {278impl<T: Config> CollectionHandle<T> {
197 Ok(())293 Ok(())
198 }294 }
295
296 fn next_token_id(&self) -> Result<uint256> {
297 Ok(ItemListIndex::get(self.id)
298 .checked_add(1)
299 .ok_or("item id overflow")?
300 .into())
301 }
199}302}
200303
201#[solidity_interface(304#[solidity_interface(
205 ERC721,308 ERC721,
206 ERC721Metadata,309 ERC721Metadata,
207 ERC721Enumerable,310 ERC721Enumerable,
208 ERC721UniqueExtensions311 ERC721UniqueExtensions,
312 ERC721Mintable,
313 ERC721Burnable,
209 )314 )
210)]315)]
211impl<T: Config> CollectionHandle<T> {}316impl<T: Config> CollectionHandle<T> {}
298#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]403#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
299impl<T: Config> CollectionHandle<T> {}404impl<T: Config> CollectionHandle<T> {}
405
406macro_rules! generate_code {
407 ($name:ident, $decl:ident, $is_impl:literal) => {
408 #[test]
409 #[ignore]
410 fn $name() {
411 use sp_std::collections::btree_set::BTreeSet;
412 let mut out = BTreeSet::new();
413 $decl::generate_solidity_interface(&mut out, $is_impl);
414 println!("=== SNIP START ===");
415 println!("// SPDX-License-Identifier: OTHER");
416 println!("// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::{} --exact --nocapture --ignored`", stringify!(name));
417 println!();
418 println!("pragma solidity >=0.8.0 <0.9.0;");
419 println!();
420 for b in out {
421 println!("{}", b);
422 }
423 println!("=== SNIP END ===");
424 }
425 };
426}
427
428// Not a tests, but code generators
429generate_code!(nft_impl, UniqueNFTCall, true);
430generate_code!(nft_iface, UniqueNFTCall, false);
431
432generate_code!(fungible_impl, UniqueFungibleCall, true);
433generate_code!(fungible_iface, UniqueFungibleCall, false);
300434
modifiedpallets/nft/src/eth/stubs/ERC20.bindiffbeforeafterboth

binary blob — no preview

modifiedpallets/nft/src/eth/stubs/ERC20.soldiffbeforeafterboth
1// SPDX-License-Identifier: OTHER1// SPDX-License-Identifier: OTHER
2// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`
23
3pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;
5
6// Common stubs holder
7contract Dummy {
8 uint8 dummy;
9 string stub_error = "this contract is implemented in native";
10}
11
12// Inline
13contract ERC20Events {
14 event Transfer(address from, address to, uint256 value);
15 event Approval(address owner, address spender, uint256 value);
16}
17
18// Inline
19contract InlineNameSymbol is Dummy {
20 function name() public view returns (string memory) {
21 require(false, stub_error);
22 dummy;
23 return "";
24 }
25 function symbol() public view returns (string memory) {
26 require(false, stub_error);
27 dummy;
28 return "";
29 }
30}
31
32// Inline
33contract InlineTotalSupply is Dummy {
34 function totalSupply() public view returns (uint256) {
35 require(false, stub_error);
36 dummy;
37 return 0;
38 }
39}
40
41contract ERC165 is Dummy {
42 function supportsInterface(uint32 interfaceId) public view returns (bool) {
43 require(false, stub_error);
44 interfaceId;
45 dummy;
46 return false;
47 }
48}
449
5contract ERC20 {50contract ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
6 uint8 _dummy = 0;
7 string stub_error = "this contract does not exists, code for collections is implemented at pallet side";
8
9 // 0x18160ddd
10 function totalSupply() external view returns (uint256) {51 function decimals() public view returns (uint8) {
11 require(false, stub_error);52 require(false, stub_error);
12 _dummy;53 dummy;
13 return 0;54 return 0;
14 }55 }
15
16 // 0x70a08231
17 function balanceOf(address account) external view returns (uint256) {56 function balanceOf(address owner) public view returns (uint256) {
18 require(false, stub_error);57 require(false, stub_error);
19 account;58 owner;
20 _dummy;59 dummy;
21 return 0;60 return 0;
22 }61 }
23
24 // 0xa9059cbb
25 function transfer(address recipient, uint256 amount) external returns (bool) {62 function transfer(address to, uint256 amount) public returns (bool) {
26 require(false, stub_error);63 require(false, stub_error);
27 recipient;64 to;
28 amount;65 amount;
29 _dummy = 0;66 dummy = 0;
30 return false;67 return false;
31 }68 }
32
33 // 0xdd62ed3e
34 function allowance(address owner, address spender) external view returns (uint256) {69 function transferFrom(address from, address to, uint256 amount) public returns (bool) {
35 require(false, stub_error);70 require(false, stub_error);
36 owner;71 from;
37 spender;72 to;
73 amount;
74 dummy = 0;
38 return _dummy;75 return false;
39 }76 }
40
41 // 0x095ea7b3
42 function approve(address spender, uint256 amount) external returns (bool) {77 function approve(address spender, uint256 amount) public returns (bool) {
43 require(false, stub_error);78 require(false, stub_error);
44 spender;79 spender;
45 amount;80 amount;
46 _dummy = 0;81 dummy = 0;
47 return false;82 return false;
48 }83 }
49
50 // 0x23b872dd
51 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {84 function allowance(address owner, address spender) public view returns (uint256) {
52 require(false, stub_error);85 require(false, stub_error);
53 sender;86 owner;
54 recipient;87 spender;
55 amount;88 dummy;
56 _dummy = 0;
57 return false;89 return 0;
58 }90 }
59
60 // While ERC165 is not required by spec of ERC20, better implement it
61 // 0x01ffc9a7
62 function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
63 return
64 // ERC20
65 interfaceID == 0x36372b07 ||
66 // ERC165
67 interfaceID == 0x01ffc9a7;
68 }
69}91}
92
93contract UniqueFungible is Dummy, ERC165, ERC20 {
94}
modifiedpallets/nft/src/eth/stubs/ERC721.bindiffbeforeafterboth

binary blob — no preview

modifiedpallets/nft/src/eth/stubs/ERC721.soldiffbeforeafterboth
1// SPDX-License-Identifier: OTHER1// SPDX-License-Identifier: OTHER
2// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`
23
3pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;
45
6// Common stubs holder
5contract ERC721 {7contract Dummy {
6 uint8 _dummy = 0;8 uint8 dummy;
7 address _dummy_addr = 0x0000000000000000000000000000000000000000;9 string stub_error = "this contract is implemented in native";
8 string _dummy_string = "";10}
9 string stub_error =11
10 "this contract does not exists, code for collections is implemented at pallet side";12// Inline
11
12 event Transfer(
13 address indexed from,
14 address indexed to,
15 uint256 indexed tokenId
16 );
17
18 event Approval(
19 address indexed owner,
20 address indexed approved,
21 uint256 indexed tokenId
22 );
23
24 event ApprovalForAll(
25 address indexed owner,
26 address indexed operator,
27 bool approved
28 );
29
30 // 0x18160ddd
31 function totalSupply() external view returns (uint256) {13contract ERC721Events {
32 require(false, stub_error);14 event Transfer(address from, address to, uint256 tokenId);
33 return 0;15 event Approval(address owner, address approved, uint256 tokenId);
16 event ApprovalForAll(address owner, address operator, bool approved);
34 }17}
3518
19// Inline
36 function name() external view returns (string memory res_name) {20contract ERC721MintableEvents {
37 require(false, stub_error);21 event MintingFinished();
38 res_name = _dummy_string;
39 }22}
4023
24// Inline
25contract InlineNameSymbol is Dummy {
41 function symbol() external view returns (string memory res_symbol) {26 function name() public view returns (string memory) {
42 require(false, stub_error);27 require(false, stub_error);
43 res_symbol = _dummy_string;28 dummy;
29 return "";
44 }30 }
45
46 function tokenURI(uint256 tokenId) external view returns (string memory) {31 function symbol() public view returns (string memory) {
47 require(false, stub_error);32 require(false, stub_error);
48 tokenId;33 dummy;
49 return _dummy_string;34 return "";
50 }35 }
5136}
37
38// Inline
39contract InlineTotalSupply is Dummy {
52 function tokenByIndex(uint256 index) external view returns (uint256) {40 function totalSupply() public view returns (uint256) {
53 require(false, stub_error);41 require(false, stub_error);
54 index;42 dummy;
55 return 0;43 return 0;
56 }44 }
5745}
46
47contract ERC165 is Dummy {
58 function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) {48 function supportsInterface(uint32 interfaceId) public view returns (bool) {
59 require(false, stub_error);49 require(false, stub_error);
60 owner;50 interfaceId;
61 index;51 dummy;
62 return 0;52 return false;
63 }53 }
6454}
65 // 0x70a0823155
56contract ERC721 is Dummy, ERC165, ERC721Events {
66 function balanceOf(address owner) external view returns (uint256) {57 function balanceOf(address owner) public view returns (uint256) {
67 require(false, stub_error);58 require(false, stub_error);
68 owner;59 owner;
60 dummy;
69 return 0;61 return 0;
70 }62 }
71
72 // 0x6352211e
73 function ownerOf(uint256 tokenId) external view returns (address) {63 function ownerOf(uint256 tokenId) public view returns (address) {
74 require(false, stub_error);64 require(false, stub_error);
75 tokenId;65 tokenId;
66 dummy;
76 return _dummy_addr;67 return 0x0000000000000000000000000000000000000000;
77 }68 }
78
79 // 0xb88d4fde
80 function safeTransferFrom(69 function safeTransferFromWithData(address from, address to, uint256 tokenId, bytes memory data) public {
81 address from,
82 address to,
83 uint256 tokenId,
84 bytes calldata data
85 ) external payable {
86 require(false, stub_error);70 require(false, stub_error);
87 from;71 from;
88 to;72 to;
89 tokenId;73 tokenId;
90 data;74 data;
75 dummy = 0;
91 }76 }
92
93 // 0x42842e0e
94 function safeTransferFrom(77 function safeTransferFrom(address from, address to, uint256 tokenId) public {
95 address from,
96 address to,
97 uint256 tokenId
98 ) external payable {
99 require(false, stub_error);78 require(false, stub_error);
100 from;79 from;
101 to;80 to;
102 tokenId;81 tokenId;
82 dummy = 0;
103 }83 }
104
105 // 0x23b872dd
106 function transferFrom(84 function transferFrom(address from, address to, uint256 tokenId) public {
107 address from,
108 address to,
109 uint256 tokenId
110 ) external payable {
111 require(false, stub_error);85 require(false, stub_error);
112 from;86 from;
113 to;87 to;
114 tokenId;88 tokenId;
89 dummy = 0;
115 }90 }
116
117 // 0x095ea7b3
118 function approve(address approved, uint256 tokenId) external payable {91 function approve(address approved, uint256 tokenId) public {
119 require(false, stub_error);92 require(false, stub_error);
120 approved;93 approved;
121 tokenId;94 tokenId;
95 dummy = 0;
122 }96 }
123
124 // 0xa22cb465
125 function setApprovalForAll(address operator, bool approved) external {97 function setApprovalForAll(address operator, bool approved) public {
126 require(false, stub_error);98 require(false, stub_error);
127 operator;99 operator;
128 approved;100 approved;
129 _dummy = 0;101 dummy = 0;
130 }102 }
131
132 // 0x081812fc
133 function getApproved(uint256 tokenId) external view returns (address) {103 function getApproved(uint256 tokenId) public view returns (address) {
134 require(false, stub_error);104 require(false, stub_error);
135 tokenId;105 tokenId;
106 dummy;
136 return _dummy_addr;107 return 0x0000000000000000000000000000000000000000;
137 }108 }
138
139 // 0xe985e9c5
140 function isApprovedForAll(address owner, address operator)109 function isApprovedForAll(address owner, address operator) public view returns (address) {
141 external
142 view
143 returns (bool)
144 {
145 require(false, stub_error);110 require(false, stub_error);
146 owner;111 owner;
147 operator;112 operator;
113 dummy;
148 return false;114 return 0x0000000000000000000000000000000000000000;
149 }115 }
150116}
151 // 0x01ffc9a7117
118contract ERC721Burnable is Dummy {
119 function burn(uint256 tokenId) public {
120 require(false, stub_error);
121 tokenId;
122 dummy = 0;
123 }
124}
125
126contract ERC721Enumerable is Dummy, InlineTotalSupply {
127 function tokenByIndex(uint256 index) public view returns (uint256) {
128 require(false, stub_error);
129 index;
130 dummy;
131 return 0;
132 }
133 function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
134 require(false, stub_error);
135 owner;
136 index;
137 dummy;
138 return 0;
139 }
140}
141
142contract ERC721Metadata is Dummy, InlineNameSymbol {
143 function tokenURI(uint256 tokenId) public view returns (string memory) {
144 require(false, stub_error);
145 tokenId;
146 dummy;
147 return "";
148 }
149}
150
151contract ERC721Mintable is Dummy, ERC721MintableEvents {
152 function mintingFinished() public view returns (bool) {
153 require(false, stub_error);
154 dummy;
155 return false;
156 }
157 function mint(address to, uint256 tokenId) public returns (bool) {
158 require(false, stub_error);
159 to;
160 tokenId;
161 dummy = 0;
162 return false;
163 }
152 function supportsInterface(bytes4 interfaceID) public pure returns (bool) {164 function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) public returns (bool) {
153 return165 require(false, stub_error);
154 // ERC721166 to;
155 interfaceID == 0x80ac58cd ||167 tokenId;
156 // ERC721Metadata168 tokenUri;
157 interfaceID == 0x5b5e139f ||
158 // ERC721Enumerable
159 interfaceID == 0x780e9d63 ||
160 // ERC165
161 interfaceID == 0x01ffc9a7;169 dummy = 0;
170 return false;
162 }171 }
163}172 function finishMinting() public returns (bool) {
164173 require(false, stub_error);
174 dummy = 0;
175 return false;
176 }
177}
178
179contract ERC721UniqueExtensions is Dummy {
180 function transfer(address to, uint256 tokenId) public {
181 require(false, stub_error);
182 to;
183 tokenId;
184 dummy = 0;
185 }
186 function nextTokenId() public view returns (uint256) {
187 require(false, stub_error);
188 dummy;
189 return 0;
190 }
191}
192
193contract UniqueNFT is Dummy, ERC165, ERC721, ERC721Metadata, ERC721Enumerable, ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable {
194}
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
177 OutOfGas,177 OutOfGas,
178 /// Collection settings not allowing items transferring178 /// Collection settings not allowing items transferring
179 TransferNotAllowed,179 TransferNotAllowed,
180 /// Can't transfer tokens to ethereum zero address
181 AddressIsZero,
180 }182 }
181}183}
182184
1248 owner: &T::CrossAccountId,1250 owner: &T::CrossAccountId,
1249 data: CreateItemData,1251 data: CreateItemData,
1250 ) -> DispatchResult {1252 ) -> DispatchResult {
1253 ensure!(
1254 owner != &T::CrossAccountId::from_eth(H160([0; 20])),
1255 Error::<T>::AddressIsZero
1256 );
1257
1251 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1258 Self::can_create_items_in_collection(collection, sender, owner, 1)?;
1252 Self::validate_create_item_args(collection, &data)?;1259 Self::validate_create_item_args(collection, &data)?;
1262 item_id: TokenId,1269 item_id: TokenId,
1263 value: u128,1270 value: u128,
1264 ) -> DispatchResult {1271 ) -> DispatchResult {
1272 ensure!(
1273 recipient != &T::CrossAccountId::from_eth(H160([0; 20])),
1274 Error::<T>::AddressIsZero
1275 );
1276
1265 // Limits check1277 // Limits check
1266 Self::is_correct_transfer(target_collection, recipient)?;1278 Self::is_correct_transfer(target_collection, recipient)?;
1829 <NftItemList<T>>::remove(collection_id, item_id);1841 <NftItemList<T>>::remove(collection_id, item_id);
1830 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1842 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);
18311843
1844 collection.log(ERC721Events::Transfer {
1845 from: *item.owner.as_eth(),
1846 to: H160::default(),
1847 token_id: item_id.into(),
1848 })?;
1832 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1849 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));
1833 Ok(())1850 Ok(())
1834 }1851 }
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
4//4//
55
6import privateKey from '../substrate/privateKey';6import privateKey from '../substrate/privateKey';
7import { approveExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE } from '../util/helpers';7import { approveExpectSuccess, burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, UNIQUE } from '../util/helpers';
8import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth } from './util/helpers';8import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, transferBalanceToEth } from './util/helpers';
9import { evmToAddress } from '@polkadot/util-crypto';
9import nonFungibleAbi from './nonFungibleAbi.json';10import nonFungibleAbi from './nonFungibleAbi.json';
10import { expect } from 'chai';11import { expect } from 'chai';
11import waitNewBlocks from '../substrate/wait-new-blocks';12import waitNewBlocks from '../substrate/wait-new-blocks';
13import { submitTransactionAsync } from '../substrate/substrate-api';
1214
13describe('NFT: Information getting', () => {15describe('NFT: Information getting', () => {
14 itWeb3('totalSupply', async ({ api, web3 }) => {16 itWeb3('totalSupply', async ({ api, web3 }) => {
64});66});
6567
66describe('NFT: Plain calls', () => {68describe('NFT: Plain calls', () => {
69 itWeb3('Can perform mint()', async ({ web3, api }) => {
70 const collection = await createCollectionExpectSuccess({
71 mode: { type: 'NFT' },
72 });
73 const alice = privateKey('//Alice');
74
75 const caller = await createEthAccountWithBalance(api, web3);
76 const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: caller });
77 await submitTransactionAsync(alice, changeAdminTx);
78 const receiver = createEthAccount(web3);
79
80 const address = collectionIdToAddress(collection);
81 const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
82
83 {
84 const nextTokenId = await contract.methods.nextTokenId().call();
85 expect(nextTokenId).to.be.equal('1');
86 const result = await contract.methods.mintWithTokenURI(
87 receiver,
88 nextTokenId,
89 'Test URI',
90 ).send({from: caller});
91 const events = normalizeEvents(result.events);
92
93 expect(events).to.be.deep.equal([
94 {
95 address,
96 event: 'Transfer',
97 args: {
98 from: '0x0000000000000000000000000000000000000000',
99 to: receiver,
100 tokenId: nextTokenId,
101 },
102 },
103 ]);
104
105 await waitNewBlocks(api, 1);
106 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
107 }
108 });
109
110 itWeb3('Can perform burn()', async ({ web3, api }) => {
111 const collection = await createCollectionExpectSuccess({
112 mode: {type: 'NFT'},
113 });
114 const alice = privateKey('//Alice');
115
116 const owner = await createEthAccountWithBalance(api, web3);
117
118 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner });
119
120 const address = collectionIdToAddress(collection);
121 const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS});
122
123 {
124 const result = await contract.methods.burn(tokenId).send({ from: owner });
125 const events = normalizeEvents(result.events);
126
127 expect(events).to.be.deep.equal([
128 {
129 address,
130 event: 'Transfer',
131 args: {
132 from: owner,
133 to: '0x0000000000000000000000000000000000000000',
134 tokenId: tokenId.toString(),
135 },
136 },
137 ]);
138 }
139 });
140
67 itWeb3('Can perform approve()', async ({ web3, api }) => {141 itWeb3('Can perform approve()', async ({ web3, api }) => {
68 const collection = await createCollectionExpectSuccess({142 const collection = await createCollectionExpectSuccess({
251});325});
252326
253describe('NFT: Substrate calls', () => {327describe('NFT: Substrate calls', () => {
328 itWeb3('Events emitted for mint()', async ({ web3 }) => {
329 const collection = await createCollectionExpectSuccess({
330 mode: { type: 'NFT' },
331 });
332 const alice = privateKey('//Alice');
333
334 const address = collectionIdToAddress(collection);
335 const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
336
337 let tokenId: number;
338 const events = await recordEvents(contract, async () => {
339 tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
340 });
341
342 expect(events).to.be.deep.equal([
343 {
344 address,
345 event: 'Transfer',
346 args: {
347 from: '0x0000000000000000000000000000000000000000',
348 to: subToEth(alice.address),
349 tokenId: tokenId!.toString(),
350 },
351 },
352 ]);
353 });
354
355 itWeb3('Events emitted for burn()', async ({ web3 }) => {
356 const collection = await createCollectionExpectSuccess({
357 mode: { type: 'NFT' },
358 });
359 const alice = privateKey('//Alice');
360
361 const address = collectionIdToAddress(collection);
362 const contract = new web3.eth.Contract(nonFungibleAbi as any, address);
363
364 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');
365 const events = await recordEvents(contract, async () => {
366 await burnItemExpectSuccess(alice, collection, tokenId);
367 });
368
369 expect(events).to.be.deep.equal([
370 {
371 address,
372 event: 'Transfer',
373 args: {
374 from: subToEth(alice.address),
375 to: '0x0000000000000000000000000000000000000000',
376 tokenId: tokenId.toString(),
377 },
378 },
379 ]);
380 });
381
254 itWeb3('Events emitted for approve()', async ({ web3 }) => {382 itWeb3('Events emitted for approve()', async ({ web3 }) => {
255 const collection = await createCollectionExpectSuccess({383 const collection = await createCollectionExpectSuccess({
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
49 "name": "ApprovalForAll",49 "name": "ApprovalForAll",
50 "type": "event"50 "type": "event"
51 },51 },
52 {
53 "anonymous": true,
54 "inputs": [],
55 "name": "MintingFinished",
56 "type": "event"
57 },
52 {58 {
53 "anonymous": false,59 "anonymous": false,
54 "inputs": [60 "inputs": [
89 ],95 ],
90 "name": "approve",96 "name": "approve",
91 "outputs": [],97 "outputs": [],
92 "stateMutability": "payable",98 "stateMutability": "nonpayable",
93 "type": "function"99 "type": "function"
94 },100 },
95 {101 {
111 "stateMutability": "view",117 "stateMutability": "view",
112 "type": "function"118 "type": "function"
113 },119 },
120 {
121 "inputs": [
122 {
123 "internalType": "uint256",
124 "name": "tokenId",
125 "type": "uint256"
126 }
127 ],
128 "name": "burn",
129 "outputs": [],
130 "stateMutability": "nonpayable",
131 "type": "function"
132 },
133 {
134 "inputs": [],
135 "name": "finishMinting",
136 "outputs": [
137 {
138 "internalType": "bool",
139 "name": "",
140 "type": "bool"
141 }
142 ],
143 "stateMutability": "nonpayable",
144 "type": "function"
145 },
114 {146 {
115 "inputs": [147 "inputs": [
116 {148 {
130 "stateMutability": "view",162 "stateMutability": "view",
131 "type": "function"163 "type": "function"
132 },164 },
133 {165 {
134 "inputs": [166 "inputs": [
135 {167 {
136 "internalType": "address",168 "internalType": "address",
144 }176 }
145 ],177 ],
146 "name": "isApprovedForAll",178 "name": "isApprovedForAll",
179 "outputs": [
180 {
181 "internalType": "address",
182 "name": "",
183 "type": "address"
184 }
185 ],
186 "stateMutability": "view",
187 "type": "function"
188 },
189 {
190 "inputs": [
191 {
192 "internalType": "address",
193 "name": "to",
194 "type": "address"
195 },
196 {
197 "internalType": "uint256",
198 "name": "tokenId",
199 "type": "uint256"
200 }
201 ],
202 "name": "mint",
147 "outputs": [203 "outputs": [
148 {204 {
149 "internalType": "bool",205 "internalType": "bool",
150 "name": "",206 "name": "",
151 "type": "bool"207 "type": "bool"
152 }208 }
153 ],209 ],
210 "stateMutability": "nonpayable",
211 "type": "function"
212 },
213 {
214 "inputs": [
215 {
216 "internalType": "address",
217 "name": "to",
218 "type": "address"
219 },
220 {
221 "internalType": "uint256",
222 "name": "tokenId",
223 "type": "uint256"
224 },
225 {
226 "internalType": "string",
227 "name": "tokenURI",
228 "type": "string"
229 }
230 ],
231 "name": "mintWithTokenURI",
232 "outputs": [
233 {
234 "internalType": "bool",
235 "name": "",
236 "type": "bool"
237 }
238 ],
239 "stateMutability": "nonpayable",
240 "type": "function"
241 },
242 {
243 "inputs": [],
244 "name": "mintingFinished",
245 "outputs": [
246 {
247 "internalType": "bool",
248 "name": "",
249 "type": "bool"
250 }
251 ],
154 "stateMutability": "view",252 "stateMutability": "view",
155 "type": "function"253 "type": "function"
156 },254 },
157 {255 {
158 "inputs": [],256 "inputs": [],
159 "name": "name",257 "name": "name",
160 "outputs": [258 "outputs": [
161 {259 {
162 "internalType": "string",260 "internalType": "string",
163 "name": "res_name",261 "name": "",
164 "type": "string"262 "type": "string"
165 }263 }
166 ],264 ],
167 "stateMutability": "view",265 "stateMutability": "view",
168 "type": "function"266 "type": "function"
169 },267 },
268 {
269 "inputs": [],
270 "name": "nextTokenId",
271 "outputs": [
272 {
273 "internalType": "uint256",
274 "name": "",
275 "type": "uint256"
276 }
277 ],
278 "stateMutability": "view",
279 "type": "function"
280 },
170 {281 {
171 "inputs": [282 "inputs": [
172 {283 {
206 ],317 ],
207 "name": "safeTransferFrom",318 "name": "safeTransferFrom",
208 "outputs": [],319 "outputs": [],
209 "stateMutability": "payable",320 "stateMutability": "nonpayable",
210 "type": "function"321 "type": "function"
211 },322 },
212 {323 {
232 "type": "bytes"343 "type": "bytes"
233 }344 }
234 ],345 ],
235 "name": "safeTransferFrom",346 "name": "safeTransferFromWithData",
236 "outputs": [],347 "outputs": [],
237 "stateMutability": "payable",348 "stateMutability": "nonpayable",
238 "type": "function"349 "type": "function"
239 },350 },
240 {351 {
258 {369 {
259 "inputs": [370 "inputs": [
260 {371 {
261 "internalType": "bytes4",372 "internalType": "uint32",
262 "name": "interfaceID",373 "name": "interfaceId",
263 "type": "bytes4"374 "type": "uint32"
264 }375 }
265 ],376 ],
266 "name": "supportsInterface",377 "name": "supportsInterface",
271 "type": "bool"382 "type": "bool"
272 }383 }
273 ],384 ],
274 "stateMutability": "pure",385 "stateMutability": "view",
275 "type": "function"386 "type": "function"
276 },387 },
277 {388 {
280 "outputs": [391 "outputs": [
281 {392 {
282 "internalType": "string",393 "internalType": "string",
283 "name": "res_symbol",394 "name": "",
284 "type": "string"395 "type": "string"
285 }396 }
286 ],397 ],
364 },475 },
365 {476 {
366 "inputs": [477 "inputs": [
367 {
368 "internalType": "address",
369 "name": "from",
370 "type": "address"
371 },
372 {478 {
373 "internalType": "address",479 "internalType": "address",
374 "name": "to",480 "name": "to",
380 "type": "uint256"486 "type": "uint256"
381 }487 }
382 ],488 ],
383 "name": "transferFrom",489 "name": "transfer",
384 "outputs": [],490 "outputs": [],
385 "stateMutability": "payable",491 "stateMutability": "nonpayable",
386 "type": "function"492 "type": "function"
387 },493 },
388 {494 {
389 "inputs": [495 "inputs": [
496 {
497 "internalType": "address",
498 "name": "from",
499 "type": "address"
500 },
390 {501 {
391 "internalType": "address",502 "internalType": "address",
392 "name": "to",503 "name": "to",
398 "type": "uint256"509 "type": "uint256"
399 }510 }
400 ],511 ],
401 "name": "transfer",512 "name": "transferFrom",
402 "outputs": [],513 "outputs": [],
403 "stateMutability": "payable",514 "stateMutability": "nonpayable",
404 "type": "function"515 "type": "function"
405 }516 }
406]517]