Skip to content
This repository was archived by the owner on Oct 2, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
8a0bc02
create mock and testing
asiniscalchi Aug 1, 2023
0fe9a63
first test
asiniscalchi Aug 1, 2023
7ebabef
testing ethereum reserved addresses
asiniscalchi Aug 1, 2023
e964668
refactoring
asiniscalchi Aug 1, 2023
aba874e
refactoring
asiniscalchi Aug 1, 2023
80dd172
refactoring tests
asiniscalchi Aug 1, 2023
9604f6c
fmt
asiniscalchi Aug 1, 2023
390359b
erc721 starting point
asiniscalchi Aug 3, 2023
64c449a
Merge branch 'dev' into feature/precompile_erc_721
asiniscalchi Aug 3, 2023
5dafd9f
fmt
asiniscalchi Aug 3, 2023
e92faae
Merge branch 'dev' into feature/precompile_erc_721
asiniscalchi Aug 3, 2023
21d6842
Merge branch 'feature/solidity_create_collection_return_an_address' i…
asiniscalchi Aug 4, 2023
e06adce
compiling
asiniscalchi Aug 4, 2023
3869690
check selectors
asiniscalchi Aug 4, 2023
d514f88
create trait Erc721
asiniscalchi Aug 4, 2023
2efff92
Erc721Precompile
asiniscalchi Aug 4, 2023
0357938
set mocks
asiniscalchi Aug 4, 2023
cec3cbf
first implermentation
asiniscalchi Aug 4, 2023
b0c10ce
first integration of erc721 in the runtime
asiniscalchi Aug 4, 2023
1420c21
fmt
asiniscalchi Aug 4, 2023
d0c4ce7
check on the collection id
asiniscalchi Aug 4, 2023
aa84dde
test on extract owner form asset_id
asiniscalchi Aug 4, 2023
7e49dae
Merge branch 'dev' into feature/precompile_erc_721
asiniscalchi Aug 5, 2023
0ec72a7
fix tests
asiniscalchi Aug 5, 2023
9be1a5b
test for erc721 trait
asiniscalchi Aug 5, 2023
2427e0d
Merge branch 'feature/precompile_erc_721' into feature/testing_precom…
asiniscalchi Aug 5, 2023
4625aad
fix compilation
asiniscalchi Aug 5, 2023
d858bf0
refactoring
asiniscalchi Aug 5, 2023
6c65ddd
returning address
asiniscalchi Aug 6, 2023
1c9e12a
return value is correctly encoded as a n Address
asiniscalchi Aug 7, 2023
c7ec2ee
Merge branch 'bug/create_collection_logs' into feature/precompile_erc…
asiniscalchi Aug 7, 2023
c39efbd
Merge branch 'feature/testing_precompiles' into feature/precompile_er…
asiniscalchi Aug 7, 2023
9dc0c14
is_erc721_contract is not member of PrecompoileSet
asiniscalchi Aug 7, 2023
b9bba2d
test on precompiled contracts
asiniscalchi Aug 7, 2023
a39432c
erc721 returns hardcoded address
asiniscalchi Aug 7, 2023
c4cd42f
refactoring
asiniscalchi Aug 7, 2023
89af0a0
testing return of asset ownership
asiniscalchi Aug 7, 2023
c6c7810
fix errors
asiniscalchi Aug 7, 2023
5cfb2b5
remove unused use
asiniscalchi Aug 7, 2023
874d2f6
create collection got an uri
asiniscalchi Aug 7, 2023
fa612ab
Merge branch 'dev' into feature/precompile_erc_721
asiniscalchi Aug 7, 2023
d3add5c
Merge branch 'dev' into feature/precompile_erc_721_token_uri
asiniscalchi Aug 7, 2023
0568c47
refactoring
asiniscalchi Aug 7, 2023
b31f1db
rewriting testing mod
asiniscalchi Aug 7, 2023
9a4f100
refactoring
asiniscalchi Aug 7, 2023
09738af
moving check for cllection address in pallet
asiniscalchi Aug 8, 2023
daf2910
collection address prefix changed
asiniscalchi Aug 8, 2023
bd47de2
test passing
asiniscalchi Aug 8, 2023
907ca7e
test passing
asiniscalchi Aug 8, 2023
10557aa
refactoring tests
asiniscalchi Aug 8, 2023
6d38abe
do not panic the runtime
asiniscalchi Aug 8, 2023
daef051
fmt
asiniscalchi Aug 8, 2023
f71eef7
solidity tokenId -> _tokenId
asiniscalchi Aug 8, 2023
15123db
erc721 functions are views
asiniscalchi Aug 8, 2023
4b0ec80
update docs
asiniscalchi Aug 8, 2023
df59324
sp-core in std , removed duplicate function
asiniscalchi Aug 8, 2023
2fbd144
documentaetion
asiniscalchi Aug 8, 2023
96d7374
using compilator type ifer
asiniscalchi Aug 8, 2023
5bf67b0
compiler infer the size of the array
asiniscalchi Aug 8, 2023
2bf2171
added to std feature
asiniscalchi Aug 8, 2023
9e1bf8d
rename variable
asiniscalchi Aug 8, 2023
bee4806
trait documentation
asiniscalchi Aug 8, 2023
7a25ce8
Merge branch 'dev' into refactoring/missing_from_precompile_erc_721
asiniscalchi Aug 9, 2023
874bfae
using pallet errors
asiniscalchi Aug 10, 2023
1322a4f
Erc721Error created
asiniscalchi Aug 10, 2023
1679acb
Erc721Error fix compilation
asiniscalchi Aug 10, 2023
e8e789f
tests green
asiniscalchi Aug 10, 2023
a2763f7
revise docuemntation
asiniscalchi Aug 10, 2023
1f3bf0c
Merge branch 'feature/using_proper_errors' into feature/precompile_er…
asiniscalchi Aug 10, 2023
3308324
fmt
asiniscalchi Aug 10, 2023
790361e
better name of constant
asiniscalchi Aug 10, 2023
dc4f211
create collection has param uri
asiniscalchi Aug 10, 2023
cc5bfdb
Merge branch 'dev' into feature/precompile_erc_721_token_uri
asiniscalchi Aug 11, 2023
2ddf57d
remove unused error
asiniscalchi Aug 11, 2023
4ef9e9c
using sp-std
asiniscalchi Aug 11, 2023
746fed6
using sp-std
asiniscalchi Aug 11, 2023
b0738dd
created CollectionBaseURI
asiniscalchi Aug 11, 2023
053a1ea
created CollectionBaseURI
asiniscalchi Aug 11, 2023
3007682
BaseURILimit is defined
asiniscalchi Aug 11, 2023
d817778
insering the base uri
asiniscalchi Aug 11, 2023
95dac77
insering the base uri
asiniscalchi Aug 11, 2023
d05b7b2
base_uri is set in storage
asiniscalchi Aug 11, 2023
f0dec74
create_collection trait has base_uri as param
asiniscalchi Aug 11, 2023
2a074d0
living_assets_ownership pallet tests are green
asiniscalchi Aug 11, 2023
ffa3835
tests passing
asiniscalchi Aug 11, 2023
c155971
test on base_uri too long
asiniscalchi Aug 11, 2023
cb2c18d
removing use
asiniscalchi Aug 11, 2023
4998cce
use Vec
asiniscalchi Aug 11, 2023
51342a0
testing minimum arguments
asiniscalchi Aug 16, 2023
c39d92f
create_collection has a BondedVec tokenURI
asiniscalchi Aug 16, 2023
a9f7053
removed unued error
asiniscalchi Aug 16, 2023
724d571
fix compilation
asiniscalchi Aug 16, 2023
1d3b5bc
refactoring
asiniscalchi Aug 16, 2023
95a3d07
refactoring
asiniscalchi Aug 16, 2023
e69a2b0
removed legacy doc
asiniscalchi Aug 16, 2023
e8fa0af
BaseURILimit type is not public
asiniscalchi Aug 16, 2023
4b87845
add transfer_from dummy tests and func definitions
magecnion Aug 16, 2023
373e1ba
traits have Error assiciated tyope
asiniscalchi Aug 16, 2023
990d44c
more generiuc signature
asiniscalchi Aug 16, 2023
e9d5b1e
fix compilation
asiniscalchi Aug 16, 2023
e76a680
test moved where concreate impl is tested
asiniscalchi Aug 16, 2023
5ac6078
Merge branch 'dev' into feature/precompile_erc_721_token_uri
asiniscalchi Aug 16, 2023
77120b4
removed harcoded buffer from tests
asiniscalchi Aug 17, 2023
01d7687
refactoring
asiniscalchi Aug 17, 2023
2396f85
checking for BaseURI bounds
asiniscalchi Aug 17, 2023
48a0b35
add transfer_from sender and receiver checks
magecnion Aug 17, 2023
c98517e
add dummy erc721 impl to pallet for compiling
magecnion Aug 17, 2023
de8cacb
Merge branch 'feature/precompile_erc_721_token_uri' of github.com:fre…
magecnion Aug 17, 2023
a5c8e78
erc721 transfer_from tests
magecnion Aug 17, 2023
f121c5c
erc721 precompile final tests
magecnion Aug 17, 2023
38f3aa8
Merge branch 'dev' of github.com:freeverseio/laos-ownership-node into…
magecnion Aug 18, 2023
5b0ee33
add transfer_from func signature to sol contract
magecnion Aug 18, 2023
e857fb0
WIP add getting initial owner for assets storage
magecnion Aug 21, 2023
a1ec297
add check to transfer_from precompile caller must be the owner
magecnion Aug 21, 2023
15d48b1
add transfer_from trait impl to pallet
magecnion Aug 21, 2023
6fcdfb4
fmt
magecnion Aug 21, 2023
200291f
owner_of trait return current owner
magecnion Aug 22, 2023
0e38420
add remix test
magecnion Aug 22, 2023
68aee06
use H160 instead of T::AcountId
magecnion Aug 22, 2023
ca229fc
Merge branch 'dev' of github.com:freeverseio/laos-ownership-node into…
magecnion Aug 22, 2023
89ffbf9
change requests
magecnion Aug 22, 2023
5fdd84b
add eth event
magecnion Aug 22, 2023
cf720b1
from u64 to H160
magecnion Aug 22, 2023
61b9d84
wip
magecnion Aug 23, 2023
6e76010
Merge branch 'dev' into feature/precompile_erc_721_transfer_from
asiniscalchi Aug 24, 2023
c1e7dc6
remove pallet_evm could from living asset pallet
magecnion Aug 24, 2023
5f52255
Merge branch 'feature/precompile_erc_721_transfer_from' of github.com…
magecnion Aug 24, 2023
6b3940d
change requests
magecnion Aug 24, 2023
3874112
Merge branch 'magecnion_change_requests' into feature/precompile_erc_…
magecnion Aug 24, 2023
191bd15
change requests
magecnion Aug 24, 2023
736aa55
change requests
magecnion Aug 24, 2023
dbb52e7
change requests
magecnion Aug 24, 2023
b7d8992
change requests
magecnion Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion pallets/living-assets-ownership/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,42 @@ pub fn convert_asset_id_to_owner(value: U256) -> H160 {

#[cfg(test)]
mod tests {
use super::*;
use crate::{functions::convert_asset_id_to_owner, H160, U256};

#[test]
fn check_convert_asset_id_to_owner() {
let value = U256::from(5);
let expected_address = H160::from_low_u64_be(5);
assert_eq!(convert_asset_id_to_owner(value), expected_address);
}

#[test]
fn check_two_assets_same_owner() {
// create two different assets
let asset1 = U256::from(
hex::decode("01C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(),
);
let asset2 = U256::from(
hex::decode("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(),
);
assert_ne!(asset1, asset2);

// check asset in decimal format
assert_eq!(
U256::from_str_radix("01C0F0f4ab324C46e55D02D0033343B4Be8A55532d", 16).unwrap(),
U256::from_dec_str("2563001357829637001682277476112176020532353127213").unwrap()
);
assert_eq!(
U256::from_str_radix("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d", 16).unwrap(),
U256::from_dec_str("5486004632491442838089647141544742059844218213165").unwrap()
);

let mut owner = [0u8; 20];
owner.copy_from_slice(
hex::decode("C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(),
);
let expected_address = H160::from(owner);
assert_eq!(convert_asset_id_to_owner(asset1), expected_address);
assert_eq!(convert_asset_id_to_owner(asset2), expected_address);
}
}
58 changes: 50 additions & 8 deletions pallets/living-assets-ownership/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod traits;

#[frame_support::pallet]
pub mod pallet {

use crate::functions::convert_asset_id_to_owner;

use super::*;
Expand Down Expand Up @@ -60,13 +61,25 @@ pub mod pallet {
pub(super) type CollectionBaseURI<T: Config> =
StorageMap<_, Blake2_128Concat, CollectionId, BaseURI<T>, OptionQuery>;

/// Asset owner
#[pallet::storage]
pub(super) type AssetOwner<T: Config> =
StorageMap<_, Blake2_128Concat, U256, H160, OptionQuery>;

fn asset_owner<T: Config>(key: U256) -> H160 {
AssetOwner::<T>::get(key).unwrap_or_else(|| convert_asset_id_to_owner(key))
}

/// Pallet events
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Collection created
/// parameters. [collection_id, who]
CollectionCreated { collection_id: CollectionId, who: T::AccountId },
/// Asset transferred to `who`
/// parameters. [asset_id_id, who]
AssetTransferred { asset_id: U256, receiver: H160 },
}

// Errors inform users that something went wrong.
Expand All @@ -75,16 +88,28 @@ pub mod pallet {
pub enum Error<T> {
/// Collection id overflow
CollectionIdOverflow,
/// Unexistent collection
UnexistentCollection,
/// Collection does not exist
CollectionDoesNotExist,
// NoPermission,
NoPermission,
// AssetDoesNotExist,
AssetDoesNotExist,
// CannotTransferSelf,
CannotTransferSelf,
// TransferToNullAddress,
TransferToNullAddress,
}

impl<T: Config> AsRef<[u8]> for Error<T> {
fn as_ref(&self) -> &[u8] {
match self {
Error::__Ignore(_, _) => b"__Ignore",
Error::CollectionIdOverflow => b"CollectionIdOverflow",
Error::UnexistentCollection => b"UnexistentCollection",
Error::CollectionDoesNotExist => b"CollectionDoesNotExist",
Error::NoPermission => b"NoPermission",
Error::AssetDoesNotExist => b"AssetDoesNotExist",
Error::CannotTransferSelf => b"CannotTransferSelf",
Error::TransferToNullAddress => b"TransferToNullAddress",
}
}
}
Expand Down Expand Up @@ -127,15 +152,32 @@ pub mod pallet {
type Error = Error<T>;

fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result<H160, Self::Error> {
match CollectionBaseURI::<T>::get(collection_id) {
Some(_) => Ok(convert_asset_id_to_owner(asset_id)),
None => Err(Error::UnexistentCollection),
}
Pallet::<T>::collection_base_uri(collection_id).ok_or(Error::CollectionDoesNotExist)?;
Ok(asset_owner::<T>(asset_id))
}

fn transfer_from(
origin: H160,
collection_id: CollectionId,
from: H160,
to: H160,
asset_id: U256,
) -> Result<(), Self::Error> {
Pallet::<T>::collection_base_uri(collection_id).ok_or(Error::CollectionDoesNotExist)?;
ensure!(origin == from, Error::NoPermission);
ensure!(asset_owner::<T>(asset_id) == from, Error::NoPermission);
ensure!(from != to, Error::CannotTransferSelf);
ensure!(to != H160::zero(), Error::TransferToNullAddress);

AssetOwner::<T>::set(asset_id, Some(to.clone()));
Self::deposit_event(Event::AssetTransferred { asset_id, receiver: to });

Ok(())
}

fn token_uri(collection_id: CollectionId, asset_id: U256) -> Result<Vec<u8>, Self::Error> {
let base_uri = Pallet::<T>::collection_base_uri(collection_id)
.ok_or(Error::UnexistentCollection)?;
.ok_or(Error::CollectionDoesNotExist)?;

// concatenate base_uri with asset_id
let mut token_uri = base_uri.to_vec();
Expand Down
1 change: 1 addition & 0 deletions pallets/living-assets-ownership/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
use sp_std::{boxed::Box, prelude::*};

type Block = frame_system::mocking::MockBlock<Test>;
type Nonce = u32;
Expand Down
125 changes: 119 additions & 6 deletions pallets/living-assets-ownership/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use core::str::FromStr;

use crate::{
address_to_collection_id, collection_id_to_address, is_collection_address, mock::*,
CollectionError, Event,
address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, AssetOwner,
CollectionBaseURI, CollectionError, Event,
};
use frame_support::assert_ok;
use sp_core::H160;

type AccountId = <Test as frame_system::Config>::AccountId;
type BaseURI = crate::BaseURI<Test>;
type AccountId = <Test as frame_system::Config>::AccountId;

const ALICE: AccountId = 0x1234;
const BOB: AccountId = 0x2234;

#[test]
fn base_uri_unexistent_collection_is_none() {
Expand Down Expand Up @@ -126,7 +127,8 @@ mod traits {
traits::{CollectionManager, Erc721},
Error, Event,
};
use frame_support::{assert_err, assert_ok};
use frame_support::{assert_err, assert_noop, assert_ok};
use sp_core::U256;

#[test]
fn base_uri_of_unexistent_collection_is_none() {
Expand Down Expand Up @@ -223,7 +225,7 @@ mod traits {
fn owner_of_asset_of_unexistent_collection_should_error() {
new_test_ext().execute_with(|| {
let result = <LivingAssetsModule as Erc721>::owner_of(0, 2.into());
assert_err!(result, Error::UnexistentCollection);
assert_err!(result, Error::CollectionDoesNotExist);
});
}

Expand All @@ -242,11 +244,122 @@ mod traits {
});
}

#[test]
fn caller_is_not_current_owner_should_fail() {
let asset_id = U256::from(5);
let sender = H160::from_str("0000000000000000000000000000000000000006").unwrap();
let receiver = H160::from_low_u64_be(BOB);
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert!(AssetOwner::<Test>::get(asset_id).is_none());
CollectionBaseURI::<Test>::insert(1, BaseURI::default());
assert_noop!(
<LivingAssetsModule as Erc721>::transfer_from(
H160::from_low_u64_be(ALICE),
1,
sender,
receiver,
asset_id,
),
Error::<Test>::NoPermission
);
});
}

#[test]
fn sender_is_not_current_owner_should_fail() {
let asset_id = U256::from(5);
let sender = H160::from_str("0000000000000000000000000000000000000006").unwrap();
let receiver = H160::from_low_u64_be(BOB);
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert!(AssetOwner::<Test>::get(asset_id).is_none());
CollectionBaseURI::<Test>::insert(1, BaseURI::default());
assert_noop!(
<LivingAssetsModule as Erc721>::transfer_from(
sender, 1, sender, receiver, asset_id,
),
Error::<Test>::NoPermission
);
});
}

#[test]
fn same_sender_and_receiver_should_fail() {
let asset_id = U256::from(5);
let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap();
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert!(AssetOwner::<Test>::get(asset_id).is_none());
CollectionBaseURI::<Test>::insert(1, BaseURI::default());
assert_noop!(
<LivingAssetsModule as Erc721>::transfer_from(sender, 1, sender, sender, asset_id,),
Error::<Test>::CannotTransferSelf
);
});
}

#[test]
fn receiver_is_the_zero_address_should_fail() {
let asset_id = U256::from(5);
let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap();
let receiver = H160::from_str("0000000000000000000000000000000000000000").unwrap();
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert!(AssetOwner::<Test>::get(asset_id).is_none());
CollectionBaseURI::<Test>::insert(1, BaseURI::default());
assert_noop!(
<LivingAssetsModule as Erc721>::transfer_from(
sender, 1, sender, receiver, asset_id,
),
Error::<Test>::TransferToNullAddress
);
});
}

#[test]
fn unexistent_collection_when_transfer_from_should_fail() {
let asset_id = U256::from(5);
let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap();
let receiver = H160::from_low_u64_be(BOB);
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert!(AssetOwner::<Test>::get(asset_id).is_none());
assert_noop!(
<LivingAssetsModule as Erc721>::transfer_from(
sender, 1, sender, receiver, asset_id,
),
Error::<Test>::CollectionDoesNotExist
);
});
}

#[test]
fn sucessful_transfer_from_trait_should_work() {
let asset_id = U256::from(
hex::decode("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(),
);
let sender = H160::from_str("C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap();
let receiver = H160::from_low_u64_be(BOB);
new_test_ext().execute_with(|| {
System::set_block_number(1);
CollectionBaseURI::<Test>::insert(1, BaseURI::default());
assert!(AssetOwner::<Test>::get(asset_id).is_none());
assert_eq!(<LivingAssetsModule as Erc721>::owner_of(1, asset_id).unwrap(), sender);
assert_ok!(<LivingAssetsModule as Erc721>::transfer_from(
sender, 1, sender, receiver, asset_id,
));
assert_eq!(AssetOwner::<Test>::get(asset_id).unwrap(), receiver);
assert_eq!(<LivingAssetsModule as Erc721>::owner_of(1, asset_id).unwrap(), receiver);
System::assert_last_event(Event::AssetTransferred { asset_id, receiver }.into());
});
}

#[test]
fn token_uri_of_unexistent_collection() {
new_test_ext().execute_with(|| {
let result = <LivingAssetsModule as Erc721>::token_uri(0, 2.into());
assert_err!(result, Error::UnexistentCollection);
assert_err!(result, Error::CollectionDoesNotExist);
});
}

Expand Down
18 changes: 17 additions & 1 deletion pallets/living-assets-ownership/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,27 @@ pub trait Erc721 {
///
/// # Arguments
///
/// * `collection_id` - The unique identifier for the collection.
/// * `asset_id` - The unique identifier for the asset within the collection.
///
/// # Returns
///
/// A `Vec<u8>` representing the URI of the asset or an error if retrieval fails.
fn token_uri(collection_id: CollectionId, asset_id: U256) -> Result<Vec<u8>, Self::Error>;

/// Transfers the ownership of a asset from one address to another address
///
/// # Arguments
///
/// * `origin` - The caller's address.
/// * `collection_id` - The unique identifier for the collection.
/// * `from` - The current owner of the asset.
/// * `to` - The new owner.
/// * `asset_id` - The unique identifier for the asset within the collection.
fn transfer_from(
origin: H160,
collection_id: CollectionId,
from: H160,
to: H160,
asset_id: U256,
) -> Result<(), Self::Error>;
}
4 changes: 4 additions & 0 deletions precompile/erc721/contracts/IERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ interface IERC721 {
function tokenURI(uint256 _tokenId) external view returns (string memory);

function ownerOf(uint256 _tokenId) external view returns (address);

event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

function transferFrom(address _from, address _to, uint256 _tokenId) external;
}
Loading