difftreelog
Merge pull request #160 from UniqueNetwork/feature/erc-mintable-burnable
in: master
Implement ERC721Burnable/ERC721Mintable
13 files changed
Cargo.lockdiffbeforeafterboth5849 "sp-std",5849 "sp-std",5850]5850]58515852[[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]586958515870[[package]]5852[[package]]5871name = "pallet-scheduler"5853name = "pallet-scheduler"5882 "sp-std",5864 "sp-std",5883]5865]58665867[[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]588458845885[[package]]5885[[package]]5886name = "pallet-session"5886name = "pallet-session"10692 "syn",10692 "syn",10693]10693]1069410695[[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]107101069410711[[package]]10695[[package]]10712name = "substrate-wasm-builder"10696name = "substrate-wasm-builder"10724 "wasm-gc-api",10708 "wasm-gc-api",10725]10709]1071010711[[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]107261072610727[[package]]10727[[package]]10728name = "subtle"10728name = "subtle"crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth74 }74 }75 }75 }7677 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 }8384 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}779178#[derive(Default)]92#[derive(Default)]109123110struct 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 }169186170 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 = self418 .args419 .iter()420 .filter(|a| !a.is_special())421 .map(MethodArg::expand_solidity_argument);401422402 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);498499 // TODO: Inline inline_is500 let solidity_is = self501 .info502 .is503 .0504 .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 = self509 .info510 .is511 .0512 .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);477516478 // let methods = self.methods.iter().map(Method::solidity_def);517 // let methods = self.methods.iter().map(Method::solidity_def);479518505 )*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_generators568 )*569 #(570 #solidity_event_generators571 )*572517 let mut out = string::new();573 let mut out = string::new();574 // In solidity interface usage (is) should be preceeded by interface definition575 // This comment helps to sort it in a set576 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 {crates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth1use 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;677struct 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}324233struct Event {43struct Event {117 }127 }118 }128 }129130 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}120145121pub struct Events {146pub struct Events {144169145 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);147174148 quote! {175 quote! {149 impl #name {176 impl #name {150 #(177 #(151 #consts178 #consts152 )*179 )*180181 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 }154197155 #[automatically_derived]198 #[automatically_derived]crates/evm-coder/src/abi.rsdiffbeforeafterboth19#[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 }374938 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 }829783 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()?))103118104 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 }111130196 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();198217199 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);229249230pub 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}308309#[cfg(test)]310pub mod test {311 use super::{AbiReader, AbiWriter};312 use hex_literal::hex;313314 #[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();320321 let mut encoder = AbiWriter::new();322 encoder.bool(&true);323 // Offset to subresult324 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();329330 assert_eq!(encoded, alternative_encoded);331332 let mut decoder = AbiReader::new(&encoded);333 assert_eq!(decoder.bool().unwrap(), true);334 assert_eq!(decoder.string().unwrap(), "test");335 }336337 #[test]338 fn mint_sample() {339 let (call, mut decoder) = AbiReader::new_call(&hex!(340 "341 50bb4e7f342 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374343 0000000000000000000000000000000000000000000000000000000000000001344 0000000000000000000000000000000000000000000000000000000000000060345 0000000000000000000000000000000000000000000000000000000000000008346 5465737420555249000000000000000000000000000000000000000000000000347 "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}288359crates/evm-coder/src/solidity.rsdiffbeforeafterboth667pub 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 false11 }12 }12}13}131414macro_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}252926solidity_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 true42 }49 }43}50}445145pub 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() == 049 }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 087 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 0100 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 0105 }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}129186130pub 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}133190134pub 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}163243164#[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 ),* );167247168 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}176256177pub 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}181262182impl<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}286287pub struct SolidityEvent<A> {288 pub name: &'static str,289 pub args: A,290}291292impl<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}190299pallets/nft/src/eth/erc.rsdiffbeforeafterboth1use 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};101311#[solidity_interface(name = "ERC165")]14#[solidity_interface(name = "ERC165")]12impl<T: Config> CollectionHandle<T> {15impl<T: Config> CollectionHandle<T> {424543#[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}5059178 }187 }179}188}189190#[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")?;195196 <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;197 Ok(())198 }199}200201#[derive(ToLog)]202pub enum ERC721MintableEvents {203 #[allow(dead_code)]204 MintingFinished {},205}206207#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]208impl<T: Config> CollectionHandle<T> {209 fn minting_finished(&self) -> Result<bool> {210 Ok(false)211 }212213 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_id221 {222 return Err("item id should be next".into());223 }224225 <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 }237238 #[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_id253 {254 return Err("item id should be next".into());255 }256257 <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 }271272 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {273 Err("not implementable".into())274 }275}180276181#[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 }295296 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}200303201#[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> {}405406macro_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}427428// Not a tests, but code generators429generate_code!(nft_impl, UniqueNFTCall, true);430generate_code!(nft_iface, UniqueNFTCall, false);431432generate_code!(fungible_impl, UniqueFungibleCall, true);433generate_code!(fungible_iface, UniqueFungibleCall, false);300434pallets/nft/src/eth/stubs/ERC20.bindiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/ERC20.soldiffbeforeafterboth1// SPDX-License-Identifier: OTHER1// SPDX-License-Identifier: OTHER2// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`233pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;56// Common stubs holder7contract Dummy {8 uint8 dummy;9 string stub_error = "this contract is implemented in native";10}1112// Inline13contract ERC20Events {14 event Transfer(address from, address to, uint256 value);15 event Approval(address owner, address spender, uint256 value);16}1718// Inline19contract 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}3132// Inline33contract InlineTotalSupply is Dummy {34 function totalSupply() public view returns (uint256) {35 require(false, stub_error);36 dummy;37 return 0;38 }39}4041contract 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}4495contract 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";89 // 0x18160ddd10 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 }1516 // 0x70a0823117 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 }2324 // 0xa9059cbb25 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 }3233 // 0xdd62ed3e34 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 }4041 // 0x095ea7b342 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 }4950 // 0x23b872dd51 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 }5960 // While ERC165 is not required by spec of ERC20, better implement it61 // 0x01ffc9a762 function supportsInterface(bytes4 interfaceID) public pure returns (bool) {63 return 64 // ERC2065 interfaceID == 0x36372b07 || 66 // ERC16567 interfaceID == 0x01ffc9a7;68 }69}91}9293contract UniqueFungible is Dummy, ERC165, ERC20 {94}pallets/nft/src/eth/stubs/ERC721.bindiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/ERC721.soldiffbeforeafterboth1// SPDX-License-Identifier: OTHER1// SPDX-License-Identifier: OTHER2// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`233pragma solidity >=0.8.0 <0.9.0;4pragma solidity >=0.8.0 <0.9.0;456// Common stubs holder5contract 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 =1110 "this contract does not exists, code for collections is implemented at pallet side";12// Inline1112 event Transfer(13 address indexed from,14 address indexed to,15 uint256 indexed tokenId16 );1718 event Approval(19 address indexed owner,20 address indexed approved,21 uint256 indexed tokenId22 );2324 event ApprovalForAll(25 address indexed owner,26 address indexed operator,27 bool approved28 );2930 // 0x18160ddd31 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}351819// Inline36 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}402324// Inline25contract 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 }4546 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}3738// Inline39contract 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}4647contract 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 // 0x70a082315556contract 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 }7172 // 0x6352211e73 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 }7879 // 0xb88d4fde80 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 data85 ) 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 }9293 // 0x42842e0e94 function safeTransferFrom(77 function safeTransferFrom(address from, address to, uint256 tokenId) public {95 address from,96 address to,97 uint256 tokenId98 ) 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 }104105 // 0x23b872dd106 function transferFrom(84 function transferFrom(address from, address to, uint256 tokenId) public {107 address from,108 address to,109 uint256 tokenId110 ) 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 }116117 // 0x095ea7b3118 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 }123124 // 0xa22cb465125 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 }131132 // 0x081812fc133 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 }138139 // 0xe985e9c5140 function isApprovedForAll(address owner, address operator)109 function isApprovedForAll(address owner, address operator) public view returns (address) {141 external142 view143 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 // 0x01ffc9a7117118contract ERC721Burnable is Dummy {119 function burn(uint256 tokenId) public {120 require(false, stub_error);121 tokenId;122 dummy = 0;123 }124}125126contract 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}141142contract 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}150151contract 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 // ERC721Enumerable159 interfaceID == 0x780e9d63 ||160 // ERC165161 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}178179contract 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}192193contract UniqueNFT is Dummy, ERC165, ERC721, ERC721Metadata, ERC721Enumerable, ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable {194}pallets/nft/src/lib.rsdiffbeforeafterboth177 OutOfGas,177 OutOfGas,178 /// Collection settings not allowing items transferring178 /// Collection settings not allowing items transferring179 TransferNotAllowed,179 TransferNotAllowed,180 /// Can't transfer tokens to ethereum zero address181 AddressIsZero,180 }182 }181}183}1821841248 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>::AddressIsZero1256 );12571251 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>::AddressIsZero1275 );12761265 // Limits check1277 // Limits check1266 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);183118431844 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 }tests/src/eth/nonFungible.test.tsdiffbeforeafterboth4//4//556import 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';121413describe('NFT: Information getting', () => {15describe('NFT: Information getting', () => {14 itWeb3('totalSupply', async ({ api, web3 }) => {16 itWeb3('totalSupply', async ({ api, web3 }) => {64});66});656766describe('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');7475 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);7980 const address = collectionIdToAddress(collection);81 const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});8283 {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);9293 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 ]);104105 await waitNewBlocks(api, 1);106 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');107 }108 });109110 itWeb3('Can perform burn()', async ({ web3, api }) => {111 const collection = await createCollectionExpectSuccess({112 mode: {type: 'NFT'},113 });114 const alice = privateKey('//Alice');115116 const owner = await createEthAccountWithBalance(api, web3);117118 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});122123 {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 });14067 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});252326253describe('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');333334 const address = collectionIdToAddress(collection);335 const contract = new web3.eth.Contract(nonFungibleAbi as any, address);336337 let tokenId: number;338 const events = await recordEvents(contract, async () => {339 tokenId = await createItemExpectSuccess(alice, collection, 'NFT');340 });341342 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 });354355 itWeb3('Events emitted for burn()', async ({ web3 }) => {356 const collection = await createCollectionExpectSuccess({357 mode: { type: 'NFT' },358 });359 const alice = privateKey('//Alice');360361 const address = collectionIdToAddress(collection);362 const contract = new web3.eth.Contract(nonFungibleAbi as any, address);363364 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT');365 const events = await recordEvents(contract, async () => {366 await burnItemExpectSuccess(alice, collection, tokenId);367 });368369 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 });381254 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({tests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth49 "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]