difftreelog
feat(evm-coder) implementation generator
in: master
8 files changed
crates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -73,6 +73,20 @@
}
}
}
+
+ fn expand_generator(&self) -> proc_macro2::TokenStream {
+ let pascal_call_name = &self.pascal_call_name;
+ quote! {
+ #pascal_call_name::generate_solidity_interface(out_set, is_impl);
+ }
+ }
+
+ fn expand_event_generator(&self) -> proc_macro2::TokenStream {
+ let name = &self.name;
+ quote! {
+ #name::generate_solidity_interface(out_set, is_impl);
+ }
+ }
}
#[derive(Default)]
@@ -109,12 +123,15 @@
struct MethodArg {
name: Ident,
+ camel_name: String,
ty: Ident,
}
impl MethodArg {
fn try_from(value: &PatType) -> syn::Result<Self> {
+ let name = parse_ident_from_pat(&value.pat)?.clone();
Ok(Self {
- name: parse_ident_from_pat(&value.pat)?.clone(),
+ camel_name: cases::camelcase::to_camel_case(&name.to_string()),
+ name,
ty: parse_ident_from_type(&value.ty, false)?.clone(),
})
}
@@ -168,10 +185,10 @@
}
fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
- let name = &self.name.to_string();
+ let camel_name = &self.camel_name.to_string();
let ty = &self.ty;
quote! {
- <NamedArgument<#ty>>::new(#name)
+ <NamedArgument<#ty>>::new(#camel_name)
}
}
}
@@ -397,7 +414,11 @@
};
let result = &self.result;
- let args = self.args.iter().map(MethodArg::expand_solidity_argument);
+ let args = self
+ .args
+ .iter()
+ .filter(|a| !a.is_special())
+ .map(MethodArg::expand_solidity_argument);
quote! {
SolidityFunction {
@@ -475,6 +496,24 @@
let call_variants_this = self.methods.iter().map(Method::expand_variant_call);
let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);
+ // TODO: Inline inline_is
+ let solidity_is = self
+ .info
+ .is
+ .0
+ .iter()
+ .chain(self.info.inline_is.0.iter())
+ .map(|is| is.name.to_string());
+ let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string());
+ let solidity_generators = self
+ .info
+ .is
+ .0
+ .iter()
+ .chain(self.info.inline_is.0.iter())
+ .map(Is::expand_generator);
+ let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);
+
// let methods = self.methods.iter().map(Method::solidity_def);
quote! {
@@ -505,18 +544,40 @@
)*
)
}
- pub fn generate_solidity_interface() -> string {
+ pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {
use evm_coder::solidity::*;
use core::fmt::Write;
let interface = SolidityInterface {
name: #solidity_name,
+ is: &["Dummy", #(
+ #solidity_is,
+ )* #(
+ #solidity_events_is,
+ )* ],
functions: (#(
#solidity_functions,
)*),
};
+ if is_impl {
+ out_set.insert("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\n".into());
+ } else {
+ out_set.insert("// Common stubs holder\ninterface Dummy {\n}\n".into());
+ }
+ #(
+ #solidity_generators
+ )*
+ #(
+ #solidity_event_generators
+ )*
+
let mut out = string::new();
- let _ = interface.format(&mut out);
- out
+ // In solidity interface usage (is) should be preceeded by interface definition
+ // This comment helps to sort it in a set
+ if #solidity_name.starts_with("Inline") {
+ out.push_str("// Inline\n");
+ }
+ let _ = interface.format(is_impl, &mut out);
+ out_set.insert(out);
}
}
impl ::evm_coder::Call for #call_name {
crates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth--- a/crates/evm-coder-macros/src/to_log.rs
+++ b/crates/evm-coder-macros/src/to_log.rs
@@ -1,3 +1,4 @@
+use inflector::cases;
use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};
use std::fmt::Write;
use quote::quote;
@@ -6,6 +7,7 @@
struct EventField {
name: Ident,
+ camel_name: String,
ty: Ident,
indexed: bool,
}
@@ -24,10 +26,18 @@
}
Ok(Self {
name: name.to_owned(),
+ camel_name: cases::camelcase::to_camel_case(&name.to_string()),
ty: ty.to_owned(),
indexed,
})
}
+ fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {
+ let camel_name = &self.camel_name;
+ let ty = &self.ty;
+ quote! {
+ <NamedArgument<#ty>>::new(#camel_name)
+ }
+ }
}
struct Event {
@@ -116,6 +126,21 @@
)*];
}
}
+
+ fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
+ let name = self.name.to_string();
+ let args = self.fields.iter().map(EventField::expand_solidity_argument);
+ quote! {
+ SolidityEvent {
+ name: #name,
+ args: (
+ #(
+ #args,
+ )*
+ ),
+ }
+ }
+ }
}
pub struct Events {
@@ -144,12 +169,30 @@
let consts = self.events.iter().map(Event::expand_consts);
let serializers = self.events.iter().map(Event::expand_serializers);
+ let solidity_name = self.name.to_string();
+ let solidity_functions = self.events.iter().map(Event::expand_solidity_function);
quote! {
impl #name {
#(
#consts
)*
+
+ pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {
+ use evm_coder::solidity::*;
+ use core::fmt::Write;
+ let interface = SolidityInterface {
+ name: #solidity_name,
+ is: &[],
+ functions: (#(
+ #solidity_functions,
+ )*),
+ };
+ let mut out = string::new();
+ out.push_str("// Inline\n");
+ let _ = interface.format(is_impl, &mut out);
+ out_set.insert(out);
+ }
}
#[automatically_derived]
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -6,37 +6,44 @@
pub trait SolidityTypeName: 'static {
fn solidity_name(writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_default(writer: &mut impl fmt::Write) -> fmt::Result;
fn is_void() -> bool {
false
}
}
macro_rules! solidity_type_name {
- ($($ty:ident => $name:expr),* $(,)?) => {
+ ($($ty:ident => $name:literal = $default:literal),* $(,)?) => {
$(
impl SolidityTypeName for $ty {
fn solidity_name(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
write!(writer, $name)
}
+ fn solidity_default(writer: &mut impl core::fmt::Write) -> core::fmt::Result {
+ write!(writer, $default)
+ }
}
)*
};
}
solidity_type_name! {
- uint8 => "uint8",
- uint32 => "uint32",
- uint128 => "uint128",
- uint256 => "uint256",
- address => "address",
- string => "memory string",
- bytes => "memory bytes",
- bool => "bool",
+ uint8 => "uint8" = "0",
+ uint32 => "uint32" = "0",
+ uint128 => "uint128" = "0",
+ uint256 => "uint256" = "0",
+ address => "address" = "0x0000000000000000000000000000000000000000",
+ string => "string memory" = "\"\"",
+ bytes => "bytes memory" = "hex\"\"",
+ bool => "bool" = "false",
}
impl SolidityTypeName for void {
fn solidity_name(_writer: &mut impl fmt::Write) -> fmt::Result {
Ok(())
}
+ fn solidity_default(_writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
fn is_void() -> bool {
true
}
@@ -44,6 +51,8 @@
pub trait SolidityArguments {
fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result;
fn is_empty(&self) -> bool {
self.len() == 0
}
@@ -61,6 +70,12 @@
Ok(())
}
}
+ fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ T::solidity_default(writer)
+ }
fn len(&self) -> usize {
if T::is_void() {
0
@@ -87,6 +102,12 @@
Ok(())
}
}
+ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ writeln!(writer, "\t\t{};", self.0)
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ T::solidity_default(writer)
+ }
fn len(&self) -> usize {
if T::is_void() {
0
@@ -100,6 +121,12 @@
fn solidity_name(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
Ok(())
}
+ fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_default(&self, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
fn len(&self) -> usize {
0
}
@@ -122,13 +149,43 @@
)* );
Ok(())
}
+ fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ for_tuples!( #(
+ Tuple.solidity_get(writer)?;
+ )* );
+ Ok(())
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ if self.is_empty() {
+ Ok(())
+ } else if self.len() == 1 {
+ for_tuples!( #(
+ Tuple.solidity_default(writer)?;
+ )* );
+ Ok(())
+ } else {
+ write!(writer, "(")?;
+ let mut first = true;
+ for_tuples!( #(
+ if !Tuple.is_empty() {
+ if !first {
+ write!(writer, ", ")?;
+ }
+ first = false;
+ Tuple.solidity_name(writer)?;
+ }
+ )* );
+ write!(writer, ")")?;
+ Ok(())
+ }
+ }
fn len(&self) -> usize {
for_tuples!( #( Tuple.len() )+* )
}
}
pub trait SolidityFunctions {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result;
}
pub enum SolidityMutability {
@@ -143,10 +200,15 @@
pub mutability: SolidityMutability,
}
impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
- write!(writer, "function {}(", self.name)?;
+ fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ write!(writer, "\tfunction {}(", self.name)?;
self.args.solidity_name(writer)?;
- write!(writer, ") external")?;
+ write!(writer, ")")?;
+ if is_impl {
+ write!(writer, " public")?;
+ } else {
+ write!(writer, " external")?;
+ }
match &self.mutability {
SolidityMutability::Pure => write!(writer, " pure")?,
SolidityMutability::View => write!(writer, " view")?,
@@ -157,7 +219,25 @@
self.result.solidity_name(writer)?;
write!(writer, ")")?;
}
- writeln!(writer, ";")
+ if is_impl {
+ writeln!(writer, " {{")?;
+ writeln!(writer, "\t\trequire(false, stub_error);")?;
+ self.args.solidity_get(writer)?;
+ match &self.mutability {
+ SolidityMutability::Pure => {}
+ SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,
+ SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,
+ }
+ if !self.result.is_empty() {
+ write!(writer, "\t\treturn ")?;
+ self.result.solidity_default(writer)?;
+ writeln!(writer, ";")?;
+ }
+ writeln!(writer, "\t}}")?;
+ } else {
+ writeln!(writer, ";")?;
+ }
+ Ok(())
}
}
@@ -165,10 +245,10 @@
impl SolidityFunctions for Tuple {
for_tuples!( where #( Tuple: SolidityFunctions ),* );
- fn solidity_name(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+ fn solidity_name(&self, is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
let mut first = false;
for_tuples!( #(
- Tuple.solidity_name(writer)?;
+ Tuple.solidity_name(is_impl, writer)?;
)* );
Ok(())
}
@@ -176,14 +256,43 @@
pub struct SolidityInterface<F: SolidityFunctions> {
pub name: &'static str,
+ pub is: &'static [&'static str],
pub functions: F,
}
impl<F: SolidityFunctions> SolidityInterface<F> {
- pub fn format(&self, out: &mut impl fmt::Write) -> fmt::Result {
- writeln!(out, "interface {} {{", self.name)?;
- self.functions.solidity_name(out)?;
+ pub fn format(&self, is_impl: bool, out: &mut impl fmt::Write) -> fmt::Result {
+ if is_impl {
+ write!(out, "contract ")?;
+ } else {
+ write!(out, "interface ")?;
+ }
+ write!(out, "{}", self.name)?;
+ if !self.is.is_empty() {
+ write!(out, " is")?;
+ for (i, n) in self.is.iter().enumerate() {
+ if i != 0 {
+ write!(out, ",")?;
+ }
+ write!(out, " {}", n)?;
+ }
+ }
+ writeln!(out, " {{")?;
+ self.functions.solidity_name(is_impl, out)?;
writeln!(out, "}}")?;
Ok(())
}
}
+
+pub struct SolidityEvent<A> {
+ pub name: &'static str,
+ pub args: A,
+}
+
+impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
+ fn solidity_name(&self, _is_impl: bool, writer: &mut impl fmt::Write) -> fmt::Result {
+ write!(writer, "\tevent {}(", self.name)?;
+ self.args.solidity_name(writer)?;
+ writeln!(writer, ");")
+ }
+}
pallets/nft/src/eth/erc.rsdiffbeforeafterboth1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use nft_data_structs::{CreateItemData, CreateNftData};4use core::convert::TryInto;5use alloc::format;6use crate::{7 Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,8 ItemListIndex,9};10use frame_support::storage::{StorageMap, StorageDoubleMap};11use pallet_evm::AddressMapping;12use super::account::CrossAccountId;13use sp_std::{vec, vec::Vec};1415#[solidity_interface(name = "ERC165")]16impl<T: Config> CollectionHandle<T> {17 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {18 Ok(match self.mode {19 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),20 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),21 _ => false,22 })23 }24}2526#[solidity_interface(name = "InlineNameSymbol")]27impl<T: Config> CollectionHandle<T> {28 fn name(&self) -> Result<string> {29 Ok(decode_utf16(self.name.iter().copied())30 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))31 .collect::<string>())32 }3334 fn symbol(&self) -> Result<string> {35 Ok(string::from_utf8_lossy(&self.token_prefix).into())36 }37}3839#[solidity_interface(name = "InlineTotalSupply")]40impl<T: Config> CollectionHandle<T> {41 fn total_supply(&self) -> Result<uint256> {42 // TODO: we do not track total amount of all tokens43 Ok(0.into())44 }45}4647#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]48impl<T: Config> CollectionHandle<T> {49 fn token_uri(&self, token_id: uint256) -> Result<string> {50 // TODO: We should standartize url prefix, maybe via offchain schema?51 Ok(format!("unique.network/{}/{}", self.id, token_id))52 }53}5455#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]56impl<T: Config> CollectionHandle<T> {57 fn token_by_index(&self, index: uint256) -> Result<uint256> {58 Ok(index)59 }6061 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {62 // TODO: Not implemetable63 Err("not implemented".into())64 }65}6667#[derive(ToLog)]68pub enum ERC721Events {69 Transfer {70 #[indexed]71 from: address,72 #[indexed]73 to: address,74 #[indexed]75 token_id: uint256,76 },77 Approval {78 #[indexed]79 owner: address,80 #[indexed]81 approved: address,82 #[indexed]83 token_id: uint256,84 },85 #[allow(dead_code)]86 ApprovalForAll {87 #[indexed]88 owner: address,89 #[indexed]90 operator: address,91 approved: bool,92 },93}9495#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]96impl<T: Config> CollectionHandle<T> {97 #[solidity(rename_selector = "balanceOf")]98 fn balance_of_nft(&self, owner: address) -> Result<uint256> {99 let owner = T::EvmAddressMapping::into_account_id(owner);100 let balance = <Balance<T>>::get(self.id, owner);101 Ok(balance.into())102 }103 fn owner_of(&self, token_id: uint256) -> Result<address> {104 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;105 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;106 Ok(*token.owner.as_eth())107 }108 fn safe_transfer_from_with_data(109 &mut self,110 _from: address,111 _to: address,112 _token_id: uint256,113 _data: bytes,114 _value: value,115 ) -> Result<void> {116 // TODO: Not implemetable117 Err("not implemented".into())118 }119 fn safe_transfer_from(120 &mut self,121 _from: address,122 _to: address,123 _token_id: uint256,124 _value: value,125 ) -> Result<void> {126 // TODO: Not implemetable127 Err("not implemented".into())128 }129130 fn transfer_from(131 &mut self,132 caller: caller,133 from: address,134 to: address,135 token_id: uint256,136 _value: value,137 ) -> Result<void> {138 let caller = T::CrossAccountId::from_eth(caller);139 let from = T::CrossAccountId::from_eth(from);140 let to = T::CrossAccountId::from_eth(to);141 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;142143 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)144 .map_err(|_| "transferFrom error")?;145 Ok(())146 }147148 fn approve(149 &mut self,150 caller: caller,151 approved: address,152 token_id: uint256,153 _value: value,154 ) -> Result<void> {155 let caller = T::CrossAccountId::from_eth(caller);156 let approved = T::CrossAccountId::from_eth(approved);157 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;158159 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)160 .map_err(|_| "approve internal")?;161 Ok(())162 }163164 fn set_approval_for_all(165 &mut self,166 _caller: caller,167 _operator: address,168 _approved: bool,169 ) -> Result<void> {170 // TODO: Not implemetable171 Err("not implemented".into())172 }173174 fn get_approved(&self, _token_id: uint256) -> Result<address> {175 // TODO: Not implemetable176 Err("not implemented".into())177 }178179 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {180 // TODO: Not implemetable181 Err("not implemented".into())182 }183}184185#[solidity_interface(name = "ERC721Burnable")]186impl<T: Config> CollectionHandle<T> {187 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {188 let caller = T::CrossAccountId::from_eth(caller);189 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;190191 <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;192 Ok(())193 }194}195196#[derive(ToLog)]197pub enum ERC721MintableEvents {198 #[allow(dead_code)]199 MintingFinished {},200}201202#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]203impl<T: Config> CollectionHandle<T> {204 fn minting_finished(&self) -> Result<bool> {205 Ok(false)206 }207208 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {209 let caller = T::CrossAccountId::from_eth(caller);210 let to = T::CrossAccountId::from_eth(to);211 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;212 if <ItemListIndex>::get(self.id)213 .checked_add(1)214 .ok_or("item id overflow")?215 != token_id216 {217 return Err("item id should be next".into());218 }219220 <Module<T>>::create_item_internal(221 &caller,222 &self,223 &to,224 CreateItemData::NFT(CreateNftData {225 const_data: vec![],226 variable_data: vec![],227 }),228 )229 .map_err(|_| "mint error")?;230 Ok(true)231 }232233 #[solidity(rename_selector = "mintWithTokenURI")]234 fn mint_with_token_uri(235 &mut self,236 caller: caller,237 to: address,238 token_id: uint256,239 token_uri: string,240 ) -> Result<bool> {241 let caller = T::CrossAccountId::from_eth(caller);242 let to = T::CrossAccountId::from_eth(to);243 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;244 if <ItemListIndex>::get(self.id)245 .checked_add(1)246 .ok_or("item id overflow")?247 != token_id248 {249 return Err("item id should be next".into());250 }251252 <Module<T>>::create_item_internal(253 &caller,254 &self,255 &to,256 CreateItemData::NFT(CreateNftData {257 const_data: token_uri.into(),258 variable_data: vec![],259 }),260 )261 .map_err(|_| "mint error")?;262 Ok(true)263 }264265 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {266 Err("not implementable".into())267 }268}269270#[solidity_interface(name = "ERC721UniqueExtensions")]271impl<T: Config> CollectionHandle<T> {272 #[solidity(rename_selector = "transfer")]273 fn transfer_nft(274 &mut self,275 caller: caller,276 to: address,277 token_id: uint256,278 _value: value,279 ) -> Result<void> {280 let caller = T::CrossAccountId::from_eth(caller);281 let to = T::CrossAccountId::from_eth(to);282 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;283284 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)285 .map_err(|_| "transfer error")?;286 Ok(())287 }288289 fn next_token_id(&self) -> Result<uint256> {290 Ok(ItemListIndex::get(self.id)291 .checked_add(1)292 .ok_or("item id overflow")?293 .into())294 }295}296297#[solidity_interface(298 name = "UniqueNFT",299 is(300 ERC165,301 ERC721,302 ERC721Metadata,303 ERC721Enumerable,304 ERC721UniqueExtensions,305 ERC721Mintable,306 ERC721Burnable,307 )308)]309impl<T: Config> CollectionHandle<T> {}310311#[derive(ToLog)]312pub enum ERC20Events {313 Transfer {314 #[indexed]315 from: address,316 #[indexed]317 to: address,318 value: uint256,319 },320 Approval {321 #[indexed]322 owner: address,323 #[indexed]324 spender: address,325 value: uint256,326 },327}328329#[solidity_interface(330 name = "ERC20",331 inline_is(InlineNameSymbol, InlineTotalSupply),332 events(ERC20Events)333)]334impl<T: Config> CollectionHandle<T> {335 fn decimals(&self) -> Result<uint8> {336 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {337 *decimals338 } else {339 unreachable!()340 })341 }342 fn balance_of(&self, owner: address) -> Result<uint256> {343 let owner = T::EvmAddressMapping::into_account_id(owner);344 let balance = <Balance<T>>::get(self.id, owner);345 Ok(balance.into())346 }347 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {348 let caller = T::CrossAccountId::from_eth(caller);349 let to = T::CrossAccountId::from_eth(to);350 let amount = amount.try_into().map_err(|_| "amount overflow")?;351352 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)353 .map_err(|_| "transfer error")?;354 Ok(true)355 }356 #[solidity(rename_selector = "transferFrom")]357 fn transfer_from_fungible(358 &mut self,359 caller: caller,360 from: address,361 to: address,362 amount: uint256,363 ) -> Result<bool> {364 let caller = T::CrossAccountId::from_eth(caller);365 let from = T::CrossAccountId::from_eth(from);366 let to = T::CrossAccountId::from_eth(to);367 let amount = amount.try_into().map_err(|_| "amount overflow")?;368369 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)370 .map_err(|_| "transferFrom error")?;371 Ok(true)372 }373 #[solidity(rename_selector = "approve")]374 fn approve_fungible(375 &mut self,376 caller: caller,377 spender: address,378 amount: uint256,379 ) -> Result<bool> {380 let caller = T::CrossAccountId::from_eth(caller);381 let spender = T::CrossAccountId::from_eth(spender);382 let amount = amount.try_into().map_err(|_| "amount overflow")?;383384 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)385 .map_err(|_| "approve internal")?;386 Ok(true)387 }388 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {389 let owner = T::CrossAccountId::from_eth(owner);390 let spender = T::CrossAccountId::from_eth(spender);391392 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())393 }394}395396#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]397impl<T: Config> CollectionHandle<T> {}1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use nft_data_structs::{CreateItemData, CreateNftData};4use core::convert::TryInto;5use alloc::format;6use crate::{7 Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,8 ItemListIndex,9};10use frame_support::storage::{StorageMap, StorageDoubleMap};11use pallet_evm::AddressMapping;12use super::account::CrossAccountId;13use sp_std::{vec, vec::Vec};1415#[solidity_interface(name = "ERC165")]16impl<T: Config> CollectionHandle<T> {17 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {18 Ok(match self.mode {19 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),20 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),21 _ => false,22 })23 }24}2526#[solidity_interface(name = "InlineNameSymbol")]27impl<T: Config> CollectionHandle<T> {28 fn name(&self) -> Result<string> {29 Ok(decode_utf16(self.name.iter().copied())30 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))31 .collect::<string>())32 }3334 fn symbol(&self) -> Result<string> {35 Ok(string::from_utf8_lossy(&self.token_prefix).into())36 }37}3839#[solidity_interface(name = "InlineTotalSupply")]40impl<T: Config> CollectionHandle<T> {41 fn total_supply(&self) -> Result<uint256> {42 // TODO: we do not track total amount of all tokens43 Ok(0.into())44 }45}4647#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]48impl<T: Config> CollectionHandle<T> {49 fn token_uri(&self, token_id: uint256) -> Result<string> {50 // TODO: We should standartize url prefix, maybe via offchain schema?51 Ok(format!("unique.network/{}/{}", self.id, token_id))52 }53}5455#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]56impl<T: Config> CollectionHandle<T> {57 fn token_by_index(&self, index: uint256) -> Result<uint256> {58 Ok(index)59 }6061 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {62 // TODO: Not implemetable63 Err("not implemented".into())64 }65}6667#[derive(ToLog)]68pub enum ERC721Events {69 Transfer {70 #[indexed]71 from: address,72 #[indexed]73 to: address,74 #[indexed]75 token_id: uint256,76 },77 Approval {78 #[indexed]79 owner: address,80 #[indexed]81 approved: address,82 #[indexed]83 token_id: uint256,84 },85 #[allow(dead_code)]86 ApprovalForAll {87 #[indexed]88 owner: address,89 #[indexed]90 operator: address,91 approved: bool,92 },93}9495#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]96impl<T: Config> CollectionHandle<T> {97 #[solidity(rename_selector = "balanceOf")]98 fn balance_of_nft(&self, owner: address) -> Result<uint256> {99 let owner = T::EvmAddressMapping::into_account_id(owner);100 let balance = <Balance<T>>::get(self.id, owner);101 Ok(balance.into())102 }103 fn owner_of(&self, token_id: uint256) -> Result<address> {104 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;105 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;106 Ok(*token.owner.as_eth())107 }108 fn safe_transfer_from_with_data(109 &mut self,110 _from: address,111 _to: address,112 _token_id: uint256,113 _data: bytes,114 _value: value,115 ) -> Result<void> {116 // TODO: Not implemetable117 Err("not implemented".into())118 }119 fn safe_transfer_from(120 &mut self,121 _from: address,122 _to: address,123 _token_id: uint256,124 _value: value,125 ) -> Result<void> {126 // TODO: Not implemetable127 Err("not implemented".into())128 }129130 fn transfer_from(131 &mut self,132 caller: caller,133 from: address,134 to: address,135 token_id: uint256,136 _value: value,137 ) -> Result<void> {138 let caller = T::CrossAccountId::from_eth(caller);139 let from = T::CrossAccountId::from_eth(from);140 let to = T::CrossAccountId::from_eth(to);141 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;142143 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)144 .map_err(|_| "transferFrom error")?;145 Ok(())146 }147148 fn approve(149 &mut self,150 caller: caller,151 approved: address,152 token_id: uint256,153 _value: value,154 ) -> Result<void> {155 let caller = T::CrossAccountId::from_eth(caller);156 let approved = T::CrossAccountId::from_eth(approved);157 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;158159 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)160 .map_err(|_| "approve internal")?;161 Ok(())162 }163164 fn set_approval_for_all(165 &mut self,166 _caller: caller,167 _operator: address,168 _approved: bool,169 ) -> Result<void> {170 // TODO: Not implemetable171 Err("not implemented".into())172 }173174 fn get_approved(&self, _token_id: uint256) -> Result<address> {175 // TODO: Not implemetable176 Err("not implemented".into())177 }178179 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {180 // TODO: Not implemetable181 Err("not implemented".into())182 }183}184185#[solidity_interface(name = "ERC721Burnable")]186impl<T: Config> CollectionHandle<T> {187 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {188 let caller = T::CrossAccountId::from_eth(caller);189 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;190191 <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;192 Ok(())193 }194}195196#[derive(ToLog)]197pub enum ERC721MintableEvents {198 #[allow(dead_code)]199 MintingFinished {},200}201202#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]203impl<T: Config> CollectionHandle<T> {204 fn minting_finished(&self) -> Result<bool> {205 Ok(false)206 }207208 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {209 let caller = T::CrossAccountId::from_eth(caller);210 let to = T::CrossAccountId::from_eth(to);211 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;212 if <ItemListIndex>::get(self.id)213 .checked_add(1)214 .ok_or("item id overflow")?215 != token_id216 {217 return Err("item id should be next".into());218 }219220 <Module<T>>::create_item_internal(221 &caller,222 &self,223 &to,224 CreateItemData::NFT(CreateNftData {225 const_data: vec![],226 variable_data: vec![],227 }),228 )229 .map_err(|_| "mint error")?;230 Ok(true)231 }232233 #[solidity(rename_selector = "mintWithTokenURI")]234 fn mint_with_token_uri(235 &mut self,236 caller: caller,237 to: address,238 token_id: uint256,239 token_uri: string,240 ) -> Result<bool> {241 let caller = T::CrossAccountId::from_eth(caller);242 let to = T::CrossAccountId::from_eth(to);243 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;244 if <ItemListIndex>::get(self.id)245 .checked_add(1)246 .ok_or("item id overflow")?247 != token_id248 {249 return Err("item id should be next".into());250 }251252 <Module<T>>::create_item_internal(253 &caller,254 &self,255 &to,256 CreateItemData::NFT(CreateNftData {257 const_data: token_uri.into(),258 variable_data: vec![],259 }),260 )261 .map_err(|_| "mint error")?;262 Ok(true)263 }264265 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {266 Err("not implementable".into())267 }268}269270#[solidity_interface(name = "ERC721UniqueExtensions")]271impl<T: Config> CollectionHandle<T> {272 #[solidity(rename_selector = "transfer")]273 fn transfer_nft(274 &mut self,275 caller: caller,276 to: address,277 token_id: uint256,278 _value: value,279 ) -> Result<void> {280 let caller = T::CrossAccountId::from_eth(caller);281 let to = T::CrossAccountId::from_eth(to);282 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;283284 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)285 .map_err(|_| "transfer error")?;286 Ok(())287 }288289 fn next_token_id(&self) -> Result<uint256> {290 Ok(ItemListIndex::get(self.id)291 .checked_add(1)292 .ok_or("item id overflow")?293 .into())294 }295}296297#[solidity_interface(298 name = "UniqueNFT",299 is(300 ERC165,301 ERC721,302 ERC721Metadata,303 ERC721Enumerable,304 ERC721UniqueExtensions,305 ERC721Mintable,306 ERC721Burnable,307 )308)]309impl<T: Config> CollectionHandle<T> {}310311#[derive(ToLog)]312pub enum ERC20Events {313 Transfer {314 #[indexed]315 from: address,316 #[indexed]317 to: address,318 value: uint256,319 },320 Approval {321 #[indexed]322 owner: address,323 #[indexed]324 spender: address,325 value: uint256,326 },327}328329#[solidity_interface(330 name = "ERC20",331 inline_is(InlineNameSymbol, InlineTotalSupply),332 events(ERC20Events)333)]334impl<T: Config> CollectionHandle<T> {335 fn decimals(&self) -> Result<uint8> {336 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {337 *decimals338 } else {339 unreachable!()340 })341 }342 fn balance_of(&self, owner: address) -> Result<uint256> {343 let owner = T::EvmAddressMapping::into_account_id(owner);344 let balance = <Balance<T>>::get(self.id, owner);345 Ok(balance.into())346 }347 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {348 let caller = T::CrossAccountId::from_eth(caller);349 let to = T::CrossAccountId::from_eth(to);350 let amount = amount.try_into().map_err(|_| "amount overflow")?;351352 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)353 .map_err(|_| "transfer error")?;354 Ok(true)355 }356 #[solidity(rename_selector = "transferFrom")]357 fn transfer_from_fungible(358 &mut self,359 caller: caller,360 from: address,361 to: address,362 amount: uint256,363 ) -> Result<bool> {364 let caller = T::CrossAccountId::from_eth(caller);365 let from = T::CrossAccountId::from_eth(from);366 let to = T::CrossAccountId::from_eth(to);367 let amount = amount.try_into().map_err(|_| "amount overflow")?;368369 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)370 .map_err(|_| "transferFrom error")?;371 Ok(true)372 }373 #[solidity(rename_selector = "approve")]374 fn approve_fungible(375 &mut self,376 caller: caller,377 spender: address,378 amount: uint256,379 ) -> Result<bool> {380 let caller = T::CrossAccountId::from_eth(caller);381 let spender = T::CrossAccountId::from_eth(spender);382 let amount = amount.try_into().map_err(|_| "amount overflow")?;383384 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)385 .map_err(|_| "approve internal")?;386 Ok(true)387 }388 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {389 let owner = T::CrossAccountId::from_eth(owner);390 let spender = T::CrossAccountId::from_eth(spender);391392 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())393 }394}395396#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]397impl<T: Config> CollectionHandle<T> {}398399macro_rules! generate_code {400 ($name:ident, $decl:ident, $is_impl:literal) => {401 #[test]402 #[ignore]403 fn $name() {404 use sp_std::collections::btree_set::BTreeSet;405 let mut out = BTreeSet::new();406 $decl::generate_solidity_interface(&mut out, $is_impl);407 println!("=== SNIP START ===");408 println!("// SPDX-License-Identifier: OTHER");409 println!("// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::{} --exact --nocapture --ignored`", stringify!(name));410 println!();411 println!("pragma solidity >=0.8.0 <0.9.0;");412 println!();413 for b in out {414 println!("{}", b);415 }416 println!("=== SNIP END ===");417 }418 };419}420421// Not a tests, but code generators422generate_code!(nft_impl, UniqueNFTCall, true);423generate_code!(nft_iface, UniqueNFTCall, false);424425generate_code!(fungible_impl, UniqueFungibleCall, true);426generate_code!(fungible_iface, UniqueFungibleCall, false);pallets/nft/src/eth/stubs/ERC20.bindiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/ERC20.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/ERC20.sol
+++ b/pallets/nft/src/eth/stubs/ERC20.sol
@@ -1,69 +1,94 @@
// SPDX-License-Identifier: OTHER
+// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`
pragma solidity >=0.8.0 <0.9.0;
-contract ERC20 {
- uint8 _dummy = 0;
- string stub_error = "this contract does not exists, code for collections is implemented at pallet side";
+// Common stubs holder
+contract Dummy {
+ uint8 dummy;
+ string stub_error = "this contract is implemented in native";
+}
- // 0x18160ddd
- function totalSupply() external view returns (uint256) {
+// Inline
+contract ERC20Events {
+ event Transfer(address from, address to, uint256 value);
+ event Approval(address owner, address spender, uint256 value);
+}
+
+// Inline
+contract InlineNameSymbol is Dummy {
+ function name() public view returns (string memory) {
require(false, stub_error);
- _dummy;
- return 0;
+ dummy;
+ return "";
}
+ function symbol() public view returns (string memory) {
+ require(false, stub_error);
+ dummy;
+ return "";
+ }
+}
- // 0x70a08231
- function balanceOf(address account) external view returns (uint256) {
+// Inline
+contract InlineTotalSupply is Dummy {
+ function totalSupply() public view returns (uint256) {
require(false, stub_error);
- account;
- _dummy;
+ dummy;
return 0;
}
+}
- // 0xa9059cbb
- function transfer(address recipient, uint256 amount) external returns (bool) {
+contract ERC165 is Dummy {
+ function supportsInterface(uint32 interfaceId) public view returns (bool) {
require(false, stub_error);
- recipient;
- amount;
- _dummy = 0;
+ interfaceId;
+ dummy;
return false;
}
+}
- // 0xdd62ed3e
- function allowance(address owner, address spender) external view returns (uint256) {
+contract ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
+ function decimals() public view returns (uint8) {
require(false, stub_error);
+ dummy;
+ return 0;
+ }
+ function balanceOf(address owner) public view returns (uint256) {
+ require(false, stub_error);
owner;
- spender;
- return _dummy;
+ dummy;
+ return 0;
+ }
+ function transfer(address to, uint256 amount) public returns (bool) {
+ require(false, stub_error);
+ to;
+ amount;
+ dummy = 0;
+ return false;
}
-
- // 0x095ea7b3
- function approve(address spender, uint256 amount) external returns (bool) {
+ function transferFrom(address from, address to, uint256 amount) public returns (bool) {
require(false, stub_error);
- spender;
+ from;
+ to;
amount;
- _dummy = 0;
+ dummy = 0;
return false;
}
-
- // 0x23b872dd
- function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
+ function approve(address spender, uint256 amount) public returns (bool) {
require(false, stub_error);
- sender;
- recipient;
+ spender;
amount;
- _dummy = 0;
+ dummy = 0;
return false;
}
+ function allowance(address owner, address spender) public view returns (uint256) {
+ require(false, stub_error);
+ owner;
+ spender;
+ dummy;
+ return 0;
+ }
+}
- // While ERC165 is not required by spec of ERC20, better implement it
- // 0x01ffc9a7
- function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
- return
- // ERC20
- interfaceID == 0x36372b07 ||
- // ERC165
- interfaceID == 0x01ffc9a7;
- }
+contract UniqueFungible is Dummy, ERC165, ERC20 {
}
\ No newline at end of file
pallets/nft/src/eth/stubs/ERC721.bindiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/ERC721.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/ERC721.sol
+++ b/pallets/nft/src/eth/stubs/ERC721.sol
@@ -1,163 +1,194 @@
// SPDX-License-Identifier: OTHER
+// This code is automatically generated with `cargo test --package pallet-nft -- eth::erc::name --exact --nocapture --ignored`
pragma solidity >=0.8.0 <0.9.0;
-contract ERC721 {
- uint8 _dummy = 0;
- address _dummy_addr = 0x0000000000000000000000000000000000000000;
- string _dummy_string = "";
- string stub_error =
- "this contract does not exists, code for collections is implemented at pallet side";
+// Common stubs holder
+contract Dummy {
+ uint8 dummy;
+ string stub_error = "this contract is implemented in native";
+}
- event Transfer(
- address indexed from,
- address indexed to,
- uint256 indexed tokenId
- );
+// Inline
+contract ERC721Events {
+ event Transfer(address from, address to, uint256 tokenId);
+ event Approval(address owner, address approved, uint256 tokenId);
+ event ApprovalForAll(address owner, address operator, bool approved);
+}
- event Approval(
- address indexed owner,
- address indexed approved,
- uint256 indexed tokenId
- );
+// Inline
+contract ERC721MintableEvents {
+ event MintingFinished();
+}
- event ApprovalForAll(
- address indexed owner,
- address indexed operator,
- bool approved
- );
+// Inline
+contract InlineNameSymbol is Dummy {
+ function name() public view returns (string memory) {
+ require(false, stub_error);
+ dummy;
+ return "";
+ }
+ function symbol() public view returns (string memory) {
+ require(false, stub_error);
+ dummy;
+ return "";
+ }
+}
- // 0x18160ddd
- function totalSupply() external view returns (uint256) {
- require(false, stub_error);
- return 0;
- }
+// Inline
+contract InlineTotalSupply is Dummy {
+ function totalSupply() public view returns (uint256) {
+ require(false, stub_error);
+ dummy;
+ return 0;
+ }
+}
- function name() external view returns (string memory res_name) {
- require(false, stub_error);
- res_name = _dummy_string;
- }
+contract ERC165 is Dummy {
+ function supportsInterface(uint32 interfaceId) public view returns (bool) {
+ require(false, stub_error);
+ interfaceId;
+ dummy;
+ return false;
+ }
+}
- function symbol() external view returns (string memory res_symbol) {
- require(false, stub_error);
- res_symbol = _dummy_string;
- }
+contract ERC721 is Dummy, ERC165, ERC721Events {
+ function balanceOf(address owner) public view returns (uint256) {
+ require(false, stub_error);
+ owner;
+ dummy;
+ return 0;
+ }
+ function ownerOf(uint256 tokenId) public view returns (address) {
+ require(false, stub_error);
+ tokenId;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+ function safeTransferFromWithData(address from, address to, uint256 tokenId, bytes memory data) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ data;
+ dummy = 0;
+ }
+ function safeTransferFrom(address from, address to, uint256 tokenId) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ dummy = 0;
+ }
+ function transferFrom(address from, address to, uint256 tokenId) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ dummy = 0;
+ }
+ function approve(address approved, uint256 tokenId) public {
+ require(false, stub_error);
+ approved;
+ tokenId;
+ dummy = 0;
+ }
+ function setApprovalForAll(address operator, bool approved) public {
+ require(false, stub_error);
+ operator;
+ approved;
+ dummy = 0;
+ }
+ function getApproved(uint256 tokenId) public view returns (address) {
+ require(false, stub_error);
+ tokenId;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+ function isApprovedForAll(address owner, address operator) public view returns (address) {
+ require(false, stub_error);
+ owner;
+ operator;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+}
- function tokenURI(uint256 tokenId) external view returns (string memory) {
- require(false, stub_error);
- tokenId;
- return _dummy_string;
- }
+contract ERC721Burnable is Dummy {
+ function burn(uint256 tokenId) public {
+ require(false, stub_error);
+ tokenId;
+ dummy = 0;
+ }
+}
- function tokenByIndex(uint256 index) external view returns (uint256) {
- require(false, stub_error);
- index;
+contract ERC721Enumerable is Dummy, InlineTotalSupply {
+ function tokenByIndex(uint256 index) public view returns (uint256) {
+ require(false, stub_error);
+ index;
+ dummy;
return 0;
- }
-
- function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) {
- require(false, stub_error);
+ }
+ function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
+ require(false, stub_error);
owner;
index;
+ dummy;
return 0;
- }
+ }
+}
- // 0x70a08231
- function balanceOf(address owner) external view returns (uint256) {
- require(false, stub_error);
- owner;
- return 0;
- }
+contract ERC721Metadata is Dummy, InlineNameSymbol {
+ function tokenUri(uint256 tokenId) public view returns (string memory) {
+ require(false, stub_error);
+ tokenId;
+ dummy;
+ return "";
+ }
+}
- // 0x6352211e
- function ownerOf(uint256 tokenId) external view returns (address) {
- require(false, stub_error);
- tokenId;
- return _dummy_addr;
- }
+contract ERC721Mintable is Dummy, ERC721MintableEvents {
+ function mintingFinished() public view returns (bool) {
+ require(false, stub_error);
+ dummy;
+ return false;
+ }
+ function mint(address to, uint256 tokenId) public returns (bool) {
+ require(false, stub_error);
+ to;
+ tokenId;
+ dummy = 0;
+ return false;
+ }
+ function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) public returns (bool) {
+ require(false, stub_error);
+ to;
+ tokenId;
+ tokenUri;
+ dummy = 0;
+ return false;
+ }
+ function finishMinting() public returns (bool) {
+ require(false, stub_error);
+ dummy = 0;
+ return false;
+ }
+}
- // 0xb88d4fde
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId,
- bytes calldata data
- ) external payable {
- require(false, stub_error);
- from;
- to;
- tokenId;
- data;
- }
-
- // 0x42842e0e
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external payable {
- require(false, stub_error);
- from;
- to;
- tokenId;
- }
-
- // 0x23b872dd
- function transferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external payable {
- require(false, stub_error);
- from;
- to;
- tokenId;
- }
+contract ERC721UniqueExtensions is Dummy {
+ function transfer(address to, uint256 tokenId) public {
+ require(false, stub_error);
+ to;
+ tokenId;
+ dummy = 0;
+ }
+ function nextTokenId() public view returns (uint256) {
+ require(false, stub_error);
+ dummy;
+ return 0;
+ }
+}
- // 0x095ea7b3
- function approve(address approved, uint256 tokenId) external payable {
- require(false, stub_error);
- approved;
- tokenId;
- }
-
- // 0xa22cb465
- function setApprovalForAll(address operator, bool approved) external {
- require(false, stub_error);
- operator;
- approved;
- _dummy = 0;
- }
-
- // 0x081812fc
- function getApproved(uint256 tokenId) external view returns (address) {
- require(false, stub_error);
- tokenId;
- return _dummy_addr;
- }
-
- // 0xe985e9c5
- function isApprovedForAll(address owner, address operator)
- external
- view
- returns (bool)
- {
- require(false, stub_error);
- owner;
- operator;
- return false;
- }
-
- // 0x01ffc9a7
- function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
- return
- // ERC721
- interfaceID == 0x80ac58cd ||
- // ERC721Metadata
- interfaceID == 0x5b5e139f ||
- // ERC721Enumerable
- interfaceID == 0x780e9d63 ||
- // ERC165
- interfaceID == 0x01ffc9a7;
- }
-}
+contract UniqueNFT is Dummy, ERC165, ERC721, ERC721Metadata, ERC721Enumerable, ERC721UniqueExtensions, ERC721Mintable, ERC721Burnable {
+}
\ No newline at end of file