use alloc::{string::String, vec, vec::Vec};
use core::{borrow::BorrowMut, marker::PhantomData};
use stylus_sdk::{
abi::Bytes,
alloy_primitives::{Address, U256},
alloy_sol_types::sol,
evm, msg,
prelude::*,
};
pub trait ERC721Params {
const NAME: &'static str;
const SYMBOL: &'static str;
fn token_uri(id: U256) -> String;
}
sol_storage! {
pub struct ERC721<T: ERC721Params> {
mapping(uint256 => address) owners;
mapping(uint256 => address) approved;
mapping(address => uint256) balance;
mapping(address => mapping(address => bool)) approved_for_all;
PhantomData<T> phantom;
}
}
sol! {
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed approved, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
error AlreadyMinted();
error InvalidTokenId(uint256 id);
error NotOwner(address from, uint256 id, address real_owner);
error NotApproved(uint256 id, address owner, address spender);
error TransferToZero(uint256 id);
error ReceiverRefused(address receiver, uint256 id);
}
#[derive(SolidityError)]
pub enum ERC721Error {
AlreadyMinted(AlreadyMinted),
InvalidTokenId(InvalidTokenId),
NotOwner(NotOwner),
NotApproved(NotApproved),
TransferToZero(TransferToZero),
ReceiverRefused(ReceiverRefused),
}
impl<T: ERC721Params> ERC721<T> {
fn _require_authorized_to_spend(&self, from: Address, id: U256) -> Result<(), ERC721Error> {
let owner = self.owner_of(id)?;
if from != owner {
return Err(ERC721Error::NotOwner(NotOwner {
from,
id,
real_owner: owner,
}));
}
if msg::sender() == owner {
return Ok(());
}
if self.approved_for_all.getter(owner).get(msg::sender()) {
return Ok(());
}
if msg::sender() == self.approved.get(id) {
return Ok(());
}
Err(ERC721Error::NotApproved(NotApproved {
owner,
spender: msg::sender(),
id,
}))
}
pub fn _transfer(&mut self, id: U256, from: Address, to: Address) -> Result<(), ERC721Error> {
let mut owner = self.owners.setter(id);
let previous_owner = owner.get();
if previous_owner != from {
return Err(ERC721Error::NotOwner(NotOwner {
from,
id,
real_owner: previous_owner,
}));
}
owner.set(to);
let mut from_balance = self.balance.setter(from);
let balance = from_balance.get() - U256::from(1);
from_balance.set(balance);
let mut to_balance = self.balance.setter(to);
let balance = to_balance.get() + U256::from(1);
to_balance.set(balance);
self.approved.delete(id);
evm::log(Transfer { from, to, id });
Ok(())
}
fn _call_receiver<S: TopLevelStorage>(
storage: &mut S,
id: U256,
from: Address,
to: Address,
data: Vec<u8>,
) -> Result<(), ERC721Error> {
if to.has_code() {
let receiver = IERC721TokenReceiver::new(to);
let received = receiver
.on_erc_721_received(storage, msg::sender(), from, id, data)
.map_err(|_| {
ERC721Error::ReceiverRefused(ReceiverRefused {
receiver: receiver.address,
id,
})
})?
.0;
if u32::from_be_bytes(received) != ERC721_RECEIVED_SELECTOR {
return Err(ERC721Error::ReceiverRefused(ReceiverRefused {
receiver: receiver.address,
id,
}));
}
}
Ok(())
}
pub fn _safe_transfer<S: TopLevelStorage + BorrowMut<Self>>(
storage: &mut S,
id: U256,
from: Address,
to: Address,
data: Vec<u8>,
) -> Result<(), ERC721Error> {
storage.borrow_mut().transfer_from(from, to, id)?;
Self::_call_receiver(storage, id, from, to, data)
}
pub fn _mint(&mut self, to: Address, id: U256) -> Result<(), ERC721Error> {
if to.is_zero() {
return Err(ERC721Error::TransferToZero(TransferToZero { id }));
}
let mut owner = self.owners.setter(id);
if !owner.is_zero() {
return Err(ERC721Error::AlreadyMinted(AlreadyMinted {}));
}
owner.set(to);
let mut to_balance = self.balance.setter(to);
let balance = to_balance.get() + U256::from(1);
to_balance.set(balance);
evm::log(Transfer {
from: Address::default(),
to,
id,
});
Ok(())
}
pub fn _safe_mint<S: TopLevelStorage + BorrowMut<Self>>(
storage: &mut S,
to: Address,
id: U256,
data: Vec<u8>,
) -> Result<(), ERC721Error> {
storage.borrow_mut()._mint(to, id)?;
Self::_call_receiver(storage, id, Address::default(), to, data)?;
Ok(())
}
pub fn _burn(&mut self, id: U256) -> Result<(), ERC721Error> {
let mut owner_setter = self.owners.setter(id);
if owner_setter.is_zero() {
return Err(ERC721Error::InvalidTokenId(InvalidTokenId { id }));
}
let owner = owner_setter.get();
if msg::sender() != owner
&& !self.approved_for_all.getter(owner).get(msg::sender())
&& msg::sender() != self.approved.get(id)
{
return Err(ERC721Error::NotApproved(NotApproved {
owner,
spender: msg::sender(),
id,
}));
}
let mut owner_balance = self.balance.setter(owner);
let balance = owner_balance.get() - U256::from(1);
owner_balance.set(balance);
owner_setter.set(Address::default());
self.approved.delete(id);
evm::log(Transfer {
from: owner,
to: Address::default(),
id,
});
Ok(())
}
}
sol_interface! {
interface IERC721TokenReceiver {
function onERC721Received(address operator, address from, uint256 id, bytes data) external returns(bytes4);
}
}
const ERC721_RECEIVED_SELECTOR: u32 = 0x150b7a02;
#[external]
impl<T: ERC721Params> ERC721<T> {
pub fn name() -> String {
T::NAME.into()
}
pub fn symbol() -> String {
T::SYMBOL.into()
}
#[selector(name = "tokenURI")]
pub fn token_uri(&self, id: U256) -> Result<String, ERC721Error> {
self.owner_of(id)?; Ok(T::token_uri(id))
}
pub fn supports_interface(interface: [u8; 4]) -> bool {
if interface == [0xff; 4] {
return false;
}
const IERC165: u32 = 0x01ffc9a7;
const IERC721: u32 = 0x80ac58cd;
const IERC721METADATA: u32 = 0x5b5e139f;
matches!(
u32::from_be_bytes(interface),
IERC165 | IERC721 | IERC721METADATA
)
}
pub fn balance_of(&self, owner: Address) -> U256 {
U256::from(self.balance.get(owner))
}
pub fn owner_of(&self, id: U256) -> Result<Address, ERC721Error> {
let owner = self.owners.get(id);
if owner.is_zero() {
return Err(ERC721Error::InvalidTokenId(InvalidTokenId { id }));
}
Ok(owner)
}
pub fn safe_transfer_from<S: TopLevelStorage + BorrowMut<Self>>(
storage: &mut S,
from: Address,
to: Address,
id: U256,
) -> Result<(), ERC721Error> {
Self::safe_transfer_from_with_data(storage, from, to, id, Bytes(vec![]))
}
#[selector(name = "safeTransferFrom")]
pub fn safe_transfer_from_with_data<S: TopLevelStorage + BorrowMut<Self>>(
storage: &mut S,
from: Address,
to: Address,
id: U256,
data: Bytes,
) -> Result<(), ERC721Error> {
if to.is_zero() {
return Err(ERC721Error::TransferToZero(TransferToZero { id }));
}
storage
.borrow_mut()
._require_authorized_to_spend(from, id)?;
Self::_safe_transfer(storage, id, from, to, data.0)
}
pub fn transfer_from(
&mut self,
from: Address,
to: Address,
id: U256,
) -> Result<(), ERC721Error> {
if to.is_zero() {
return Err(ERC721Error::TransferToZero(TransferToZero { id }));
}
self._require_authorized_to_spend(from, id)?;
self._transfer(id, from, to)?;
Ok(())
}
pub fn approve(&mut self, approved: Address, id: U256) -> Result<(), ERC721Error> {
let owner = self.owner_of(id)?;
if msg::sender() != owner && !self.approved_for_all.getter(owner).get(msg::sender()) {
return Err(ERC721Error::NotApproved(NotApproved {
owner,
spender: msg::sender(),
id,
}));
}
self.approved.insert(id, approved);
evm::log(Approval {
approved,
owner,
id,
});
Ok(())
}
pub fn set_approval_for_all(&mut self, operator: Address, approved: bool) {
let owner = msg::sender();
self.approved_for_all
.setter(owner)
.insert(operator, approved);
evm::log(ApprovalForAll {
owner,
operator,
approved,
});
}
pub fn get_approved(&mut self, id: U256) -> Address {
self.approved.get(id)
}
pub fn is_approved_for_all(&mut self, owner: Address, operator: Address) -> bool {
self.approved_for_all.getter(owner).get(operator)
}
}