rustmate/tokens/
erc721.rs

1//! Provides an implementation of the ERC-721 standard.
2//!
3//! The eponymous [`ERC721`] type provides all the standard methods,
4//! and is intended to be inherited by other contract types.
5//!
6//! You can configure the behavior of [`ERC721`] via the [`ERC721Params`] trait,
7//! which allows specifying the name, symbol, and token uri.
8//!
9//! Note that this code is unaudited and not fit for production use.
10
11use alloc::{string::String, vec::Vec};
12use alloy_primitives::{Address, U256};
13use alloy_sol_types::{sol, SolError};
14use core::{borrow::BorrowMut, marker::PhantomData};
15use stylus_sdk::{abi::Bytes, evm, msg, prelude::*};
16
17pub trait ERC721Params {
18    const NAME: &'static str;
19
20    const SYMBOL: &'static str;
21
22    fn token_uri(id: U256) -> String;
23}
24
25sol_storage! {
26    /// ERC721 implements all ERC-721 methods
27    pub struct ERC721<T: ERC721Params> {
28        mapping(uint256 => address) owner_of;
29        mapping(address => uint256) balance_of;
30        mapping(uint256 => address) get_approved;
31        mapping(address => mapping(address => bool)) is_approved_for_all;
32        PhantomData<T> phantom;
33    }
34}
35
36// Declare events and Solidity error types
37sol! {
38    event Transfer(address indexed from, address indexed to, uint256 indexed id);
39    event Approval(address indexed owner, address indexed spender, uint256 indexed id);
40    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
41
42    error NotMinted();
43    error ZeroAddress();
44    error NotAuthorized();
45    error WrongFrom();
46    error InvalidRecipient();
47    error UnsafeRecipient();
48    error AlreadyMinted();
49}
50
51/// Represents the ways methods may fail.
52pub enum ERC721Error {
53    NotMinted(NotMinted),
54    ZeroAddress(ZeroAddress),
55    NotAuthorized(NotAuthorized),
56    WrongFrom(WrongFrom),
57    InvalidRecipient(InvalidRecipient),
58    UnsafeRecipient(UnsafeRecipient),
59    CallFailed(stylus_sdk::call::Error),
60    AlreadyMinted(AlreadyMinted),
61}
62
63impl From<stylus_sdk::call::Error> for ERC721Error {
64    fn from(err: stylus_sdk::call::Error) -> Self {
65        Self::CallFailed(err)
66    }
67}
68
69/// We will soon provide a `#[derive(SolidityError)]` to clean this up.
70impl From<ERC721Error> for Vec<u8> {
71    fn from(val: ERC721Error) -> Self {
72        match val {
73            ERC721Error::NotMinted(err) => err.encode(),
74            ERC721Error::ZeroAddress(err) => err.encode(),
75            ERC721Error::NotAuthorized(err) => err.encode(),
76            ERC721Error::WrongFrom(err) => err.encode(),
77            ERC721Error::InvalidRecipient(err) => err.encode(),
78            ERC721Error::UnsafeRecipient(err) => err.encode(),
79            ERC721Error::CallFailed(err) => err.into(),
80            ERC721Error::AlreadyMinted(err) => err.encode(),
81        }
82    }
83}
84
85/// Simplifies the result type for the contract's methods.
86type Result<T, E = ERC721Error> = core::result::Result<T, E>;
87
88impl<T: ERC721Params> ERC721<T> {
89    fn call_receiver<S: TopLevelStorage>(
90        storage: &mut S,
91        id: U256,
92        from: Address,
93        to: Address,
94        data: Vec<u8>,
95    ) -> Result<()> {
96        if to.has_code() {
97            let receiver = IERC721TokenReceiver::new(to);
98            let received = receiver
99                .on_erc_721_received(&mut *storage, msg::sender(), from, id, data)?
100                .0;
101
102            // 0x150b7a02 = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
103            if u32::from_be_bytes(received) != 0x150b7a02 {
104                return Err(ERC721Error::UnsafeRecipient(UnsafeRecipient {}));
105            }
106        }
107        Ok(())
108    }
109
110    pub fn safe_transfer<S: TopLevelStorage + BorrowMut<Self>>(
111        storage: &mut S,
112        id: U256,
113        from: Address,
114        to: Address,
115        data: Vec<u8>,
116    ) -> Result<()> {
117        storage.borrow_mut().transfer_from(from, to, id)?;
118        Self::call_receiver(storage, id, from, to, data)
119    }
120
121    pub fn mint(&mut self, to: Address, id: U256) -> Result<()> {
122        if to.is_zero() {
123            return Err(ERC721Error::InvalidRecipient(InvalidRecipient {}));
124        }
125
126        if self.owner_of.get(id) != Address::ZERO {
127            return Err(ERC721Error::AlreadyMinted(AlreadyMinted {}));
128        }
129
130        let mut to_balance = self.balance_of.setter(to);
131        let balance = to_balance.get() + U256::from(1);
132        to_balance.set(balance);
133
134        self.owner_of.setter(id).set(to);
135
136        evm::log(Transfer {
137            from: Address::ZERO,
138            to: to,
139            id: id,
140        });
141
142        Ok(())
143    }
144
145    pub fn burn(&mut self, id: U256) -> Result<()> {
146        let owner = self.owner_of.get(id);
147
148        if owner.is_zero() {
149            return Err(ERC721Error::NotMinted(NotMinted {}));
150        }
151
152        let mut owner_balance = self.balance_of.setter(owner);
153        let balance = owner_balance.get() - U256::from(1);
154        owner_balance.set(balance);
155
156        self.owner_of.delete(id);
157
158        self.get_approved.delete(id);
159
160        evm::log(Transfer {
161            from: owner,
162            to: Address::ZERO,
163            id: id,
164        });
165
166        Ok(())
167    }
168
169    pub fn safe_mint<S: TopLevelStorage>(
170        &mut self,
171        storage: &mut S,
172        to: Address,
173        id: U256,
174    ) -> Result<()> {
175        Self::mint(self, to, id)?;
176
177        Self::call_receiver(storage, id, Address::ZERO, to, vec![])?;
178
179        Ok(())
180    }
181
182    pub fn safe_mint_with_data<S: TopLevelStorage>(
183        &mut self,
184        storage: &mut S,
185        to: Address,
186        id: U256,
187        data: Bytes,
188    ) -> Result<()> {
189        Self::mint(self, to, id)?;
190
191        Self::call_receiver(storage, id, Address::ZERO, to, data.0)?;
192
193        Ok(())
194    }
195}
196
197#[external]
198impl<T: ERC721Params> ERC721<T> {
199    pub fn name() -> Result<String> {
200        Ok(T::NAME.into())
201    }
202
203    pub fn symbol() -> Result<String> {
204        Ok(T::SYMBOL.into())
205    }
206
207    pub fn owner_of(&self, id: U256) -> Result<Address> {
208        let owner = self.owner_of.get(id);
209
210        if owner.is_zero() {
211            return Err(ERC721Error::NotMinted(NotMinted {}));
212        }
213
214        Ok(owner)
215    }
216
217    pub fn balance_of(&self, owner: Address) -> Result<U256> {
218        if owner.is_zero() {
219            return Err(ERC721Error::ZeroAddress(ZeroAddress {}));
220        }
221
222        Ok(self.balance_of.get(owner))
223    }
224
225    pub fn get_approved(&self, id: U256) -> Result<Address> {
226        Ok(self.get_approved.get(id))
227    }
228
229    pub fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {
230        Ok(self.is_approved_for_all.getter(owner).get(operator))
231    }
232
233    #[selector(name = "tokenURI")]
234    pub fn token_uri(&self, id: U256) -> Result<String> {
235        Ok(T::token_uri(id))
236    }
237
238    pub fn approve(&mut self, spender: Address, id: U256) -> Result<()> {
239        let owner = self.owner_of.get(id);
240
241        if msg::sender() != owner || !self.is_approved_for_all.getter(owner).get(msg::sender()) {
242            return Err(ERC721Error::NotAuthorized(NotAuthorized {}));
243        }
244
245        self.get_approved.setter(id).set(spender);
246
247        evm::log(Approval {
248            owner: owner,
249            spender: spender,
250            id: id,
251        });
252
253        Ok(())
254    }
255
256    pub fn set_approval_for_all(&mut self, operator: Address, approved: bool) -> Result<()> {
257        self.is_approved_for_all
258            .setter(msg::sender())
259            .insert(operator, approved);
260
261        evm::log(ApprovalForAll {
262            owner: msg::sender(),
263            operator: operator,
264            approved: approved,
265        });
266
267        Ok(())
268    }
269
270    pub fn transfer_from(&mut self, from: Address, to: Address, id: U256) -> Result<()> {
271        if from != self.owner_of.get(id) {
272            return Err(ERC721Error::WrongFrom(WrongFrom {}));
273        }
274
275        if to.is_zero() {
276            return Err(ERC721Error::InvalidRecipient(InvalidRecipient {}));
277        }
278
279        if msg::sender() != from
280            && !self.is_approved_for_all.getter(from).get(msg::sender())
281            && msg::sender() != self.get_approved.get(id)
282        {
283            return Err(ERC721Error::NotAuthorized(NotAuthorized {}));
284        }
285
286        let mut from_balance = self.balance_of.setter(from);
287        let balance = from_balance.get() - U256::from(1);
288        from_balance.set(balance);
289
290        let mut to_balance = self.balance_of.setter(to);
291        let balance = to_balance.get() + U256::from(1);
292        to_balance.set(balance);
293
294        self.owner_of.setter(id).set(to);
295
296        self.get_approved.delete(id);
297
298        evm::log(Transfer {
299            from: from,
300            to: to,
301            id: id,
302        });
303
304        Ok(())
305    }
306
307    pub fn safe_transfer_from<S: TopLevelStorage + BorrowMut<Self>>(
308        storage: &mut S,
309        from: Address,
310        to: Address,
311        id: U256,
312    ) -> Result<()> {
313        Self::safe_transfer_from_with_data(storage, from, to, id, Bytes(vec![]))
314    }
315
316    #[selector(name = "safeTransferFrom")]
317    pub fn safe_transfer_from_with_data<S: TopLevelStorage + BorrowMut<Self>>(
318        storage: &mut S,
319        from: Address,
320        to: Address,
321        id: U256,
322        data: Bytes,
323    ) -> Result<()> {
324        Self::safe_transfer(storage, id, from, to, data.0)
325    }
326
327    pub fn supports_interface(interface: [u8; 4]) -> Result<bool> {
328        let supported = interface == 0x01ffc9a7u32.to_be_bytes() // ERC165 Interface ID for ERC165
329            || interface == 0x80ac58cdu32.to_be_bytes() // ERC165 Interface ID for ERC721
330            || interface == 0x780e9d63u32.to_be_bytes(); // ERC165 Interface ID for ERC721Metadata
331        Ok(supported)
332    }
333}
334
335sol_interface! {
336    interface IERC721TokenReceiver {
337        function onERC721Received(address operator, address from, uint256 id, bytes data) external returns(bytes4);
338    }
339}