From ed85c4ca029f87ca8df2f21b210e8b30c88c9047 Mon Sep 17 00:00:00 2001 From: Joseph Zhao Date: Tue, 17 Dec 2024 11:30:59 +0800 Subject: [PATCH 1/2] init --- contracts/src/token/erc721/extensions/mod.rs | 1 + .../src/token/erc721/extensions/wrapper.rs | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 contracts/src/token/erc721/extensions/wrapper.rs diff --git a/contracts/src/token/erc721/extensions/mod.rs b/contracts/src/token/erc721/extensions/mod.rs index 6a49373d5..ee921de88 100644 --- a/contracts/src/token/erc721/extensions/mod.rs +++ b/contracts/src/token/erc721/extensions/mod.rs @@ -4,6 +4,7 @@ pub mod consecutive; pub mod enumerable; pub mod metadata; pub mod uri_storage; +pub mod wrapper; pub use burnable::IErc721Burnable; pub use enumerable::{Erc721Enumerable, IErc721Enumerable}; diff --git a/contracts/src/token/erc721/extensions/wrapper.rs b/contracts/src/token/erc721/extensions/wrapper.rs new file mode 100644 index 000000000..61e69ea26 --- /dev/null +++ b/contracts/src/token/erc721/extensions/wrapper.rs @@ -0,0 +1,85 @@ +//! Extension of the ERC-721 token contract to support token wrapping. +//! +//! Users can deposit and withdraw an "underlying token" and receive a "wrapped +//! token" with a matching tokenId. This is useful in conjunction with other +//! modules. +use alloc::{vec, vec::Vec}; + +use alloy_primitives::{Address, U256}; +use stylus_sdk::{contract, prelude::storage, stylus_proc::SolidityError}; + +use crate::token::{ + erc721, + erc721::{ERC721IncorrectOwner, Erc721, IErc721}, +}; + +/// State of an [`Erc721Wrapper`] token. +#[storage] +pub struct Erc721Wrapper { + /// Erc721 contract storage. + pub _underlying: Erc721, + /// The ERC-721 token. + pub erc721: Erc721, +} + +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// The received ERC-721 token couldn't be wrapped. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC721UnsupportedToken(uint256 token_id); + } +} + +/// An [`Erc721Wrapper`] error. +#[derive(SolidityError, Debug)] +pub enum Error { + /// Error type from [`Erc721`] contract [`erc721::Error`]. + Erc721(erc721::Error), + /// The received ERC-721 token couldn't be wrapped. + ERC721UnsupportedToken(ERC721UnsupportedToken), +} + +impl Erc721Wrapper { + /// Wraps an ERC-721 token. + pub fn deposit_for( + &mut self, + account: Address, + token_ids: Vec, + ) -> bool { + let length = token_ids.len(); + + true + } + + /// Returns the underlying token. + pub fn underlying(&self) -> &Erc721 { + &self._underlying + } +} + +// ************** ERC-721 Internal ************** + +impl Erc721Wrapper { + fn _recover( + &mut self, + account: Address, + token_id: U256, + ) -> Result { + let owner = self.underlying().owner_of(token_id)?; + if owner != contract::address() { + return Err(erc721::Error::IncorrectOwner(ERC721IncorrectOwner { + sender: contract::address(), + token_id, + owner, + }) + .into()); + } + self.erc721._safe_mint(account, token_id, &vec![].into())?; + Ok(token_id) + } +} From a44545c56a69935a2895e7962f3464d711965e4b Mon Sep 17 00:00:00 2001 From: Joseph Zhao Date: Sat, 11 Jan 2025 20:59:25 +0800 Subject: [PATCH 2/2] fix _underlying type --- .../src/token/erc721/extensions/wrapper.rs | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/contracts/src/token/erc721/extensions/wrapper.rs b/contracts/src/token/erc721/extensions/wrapper.rs index 61e69ea26..eb5decc4b 100644 --- a/contracts/src/token/erc721/extensions/wrapper.rs +++ b/contracts/src/token/erc721/extensions/wrapper.rs @@ -6,22 +6,30 @@ use alloc::{vec, vec::Vec}; use alloy_primitives::{Address, U256}; -use stylus_sdk::{contract, prelude::storage, stylus_proc::SolidityError}; +use stylus_sdk::{ + call::Call, + contract, + prelude::storage, + storage::{StorageAddress, TopLevelStorage}, + stylus_proc::SolidityError, +}; use crate::token::{ erc721, - erc721::{ERC721IncorrectOwner, Erc721, IErc721}, + erc721::{ERC721IncorrectOwner, Erc721}, }; /// State of an [`Erc721Wrapper`] token. #[storage] pub struct Erc721Wrapper { /// Erc721 contract storage. - pub _underlying: Erc721, + pub _underlying: StorageAddress, /// The ERC-721 token. pub erc721: Erc721, } +unsafe impl TopLevelStorage for Erc721Wrapper {} + pub use sol::*; #[cfg_attr(coverage_nightly, coverage(off))] mod sol { @@ -44,8 +52,23 @@ pub enum Error { ERC721UnsupportedToken(ERC721UnsupportedToken), } +pub use token::IErc721; +mod token { + #![allow(missing_docs)] + #![cfg_attr(coverage_nightly, coverage(off))] + use alloc::vec; + + stylus_sdk::stylus_proc::sol_interface! { + /// Interface of the ERC-721 token. + interface IErc721 { + function ownerOf(uint256 token_id) external view returns (address); + } + } +} + impl Erc721Wrapper { - /// Wraps an ERC-721 token. + /// Allow a user to deposit underlying tokens and mint the corresponding + /// tokenIds. pub fn deposit_for( &mut self, account: Address, @@ -56,9 +79,21 @@ impl Erc721Wrapper { true } + /// Allow a user to burn wrapped tokens and withdraw the corresponding + /// tokenIds of the underlying tokens. + pub fn withdraw_to( + &mut self, + account: Address, + token_ids: Vec, + ) -> bool { + let length = token_ids.len(); + + true + } + /// Returns the underlying token. - pub fn underlying(&self) -> &Erc721 { - &self._underlying + pub fn underlying(&self) -> Address { + self._underlying.get() } } @@ -70,7 +105,11 @@ impl Erc721Wrapper { account: Address, token_id: U256, ) -> Result { - let owner = self.underlying().owner_of(token_id)?; + let underlying = IErc721::new(self.underlying()); + let owner = match underlying.owner_of(Call::new_in(self), token_id) { + Ok(owner) => owner, + Err(e) => return Err(Error::Erc721(e.into())), + }; if owner != contract::address() { return Err(erc721::Error::IncorrectOwner(ERC721IncorrectOwner { sender: contract::address(), @@ -83,3 +122,12 @@ impl Erc721Wrapper { Ok(token_id) } } + +#[cfg(all(test, feature = "std"))] +mod tests { + use alloy_primitives::{address, uint, Address, U256}; + use stylus_sdk::msg; + + #[motsu::test] + fn recover(contract: Erc721Wrapper) {} +}