Skip to content

Commit

Permalink
update: added documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
18aaddy committed Jan 23, 2025
1 parent 86a9bb9 commit 80d3903
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 55 deletions.
2 changes: 1 addition & 1 deletion contracts/src/token/erc721/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pub mod burnable;
pub mod consecutive;
pub mod enumerable;
pub mod metadata;
pub mod uri_storage;
pub mod royalty;
pub mod uri_storage;

pub use burnable::IErc721Burnable;
pub use enumerable::{Erc721Enumerable, IErc721Enumerable};
Expand Down
210 changes: 156 additions & 54 deletions contracts/src/token/erc721/extensions/royalty.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
// TODO: Write Documentation

//! Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
//! Implementation of the NFT Royalty Standard, a standardized way to retrieve
//! royalty payment information.
//!
//! Royalty information can be specified globally for all token ids via
//! {_setDefaultRoyalty}, and/or individually for specific token ids via
//! {_setTokenRoyalty}. The latter takes precedence over the first.
//!
//! Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
//! specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
//! Royalty is specified as a fraction of sale price. {_feeDenominator} is
//! overridable but defaults to 10000, meaning the fee is specified in basis
//! points by default.
//!
//! Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
//! fee is specified in basis points by default.
//! IMPORTANT: ERC-2981 only specifies a way to signal royalty information and
//! does not enforce its payment.
//!
//! IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
//! https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the ERC. Marketplaces are expected to
//! voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
//! See `<https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments>` in the ERC.
//! Marketplaces are expected to voluntarily pay royalties together with sales,
//! but note that this standard is not yet widely supported.
use alloy_primitives::{FixedBytes, U256, Address};
use alloy_primitives::{Address, FixedBytes, U256};
use openzeppelin_stylus_proc::interface_id;
pub use sol::*;
use stylus_sdk::{
prelude::{storage, Erase},
storage::{StorageAddress, StorageMap},
stylus_proc::SolidityError,
};
};

use crate::utils::{introspection::erc165::IErc165, structs::checkpoints::{Size, S160}};
use crate::utils::{
introspection::erc165::IErc165,
structs::checkpoints::{Size, S160},
};

type U96 = <S160 as Size>::Key;
type StorageU96 = <S160 as Size>::KeyStorage;
Expand All @@ -31,69 +38,94 @@ mod sol {
use alloy_sol_macro::sol;

sol! {

#[derive(Debug)]
#[allow(missing_docs)]
error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);

#[derive(Debug)]
#[allow(missing_docs)]
error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);

#[derive(Debug)]
#[allow(missing_docs)]
error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);

#[derive(Debug)]
#[allow(missing_docs)]
error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);

}
}

/// An [`Erc721Royalty`] extension error.
#[derive(SolidityError, Debug)]
pub enum Error {
/// The default royalty set is invalid (eg. (numerator / denominator) >= 1).
/// The default royalty set is invalid (eg. (numerator / denominator) >=
/// 1).
InvalidDefaultRoyalty(ERC2981InvalidDefaultRoyalty),

/// The default royalty receiver is invalid.
InvalidDefaultRoyaltyReceiver(ERC2981InvalidDefaultRoyaltyReceiver),

/// The royalty set for an specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).

/// The royalty set for an specific `tokenId` is invalid (eg. (numerator /
/// denominator) >= 1).
InvalidTokenRoyalty(ERC2981InvalidTokenRoyalty),

/// The royalty receiver for `tokenId` is invalid.
InvalidTokenRoyaltyReceiver(ERC2981InvalidTokenRoyaltyReceiver),
}

/// Struct for Royalty Information for tokens.
///
/// # Fields
///
/// * `receiver` - The receiver address for royalty
/// * `royalty_fraction` - Fraction of royalty for receiver
#[storage]
#[derive(Erase)]
struct RoyaltyInfo {
pub struct RoyaltyInfo {
receiver: StorageAddress,
royalty_fraction: StorageU96, // U96 should be used
}

/// State of a Royalty extension.
#[storage]
pub struct Erc721Royalty {

/// The default royalty information for all tokens.
#[allow(clippy::used_underscore_binding)]
pub _default_royalty_info: RoyaltyInfo,

pub _default_royalty_info: RoyaltyInfo,
/// The royalty information for a particular token.
#[allow(clippy::used_underscore_binding)]
pub _token_royalty_info: StorageMap<U256, RoyaltyInfo>,
}

// This is the interface of the optional `Royalty` extension
/// of the ERC-721 standard.
#[interface_id]
pub trait IErc721Royalty {
fn royalty_info(&self, token_id: U256, sale_price: U256) -> (Address, U256);
/// Returns how much royalty is owed and to whom, based on a sale price that
/// may be denominated in any unit of exchange.
///
/// The royalty amount is denominated and should be paid in that same unit
/// of exchange.
///
/// NOTE: ERC-2981 allows setting the royalty to 100% of the price.
/// In that case all the price would be sent to the royalty receiver and 0
/// tokens to the seller. Contracts dealing with royalty should consider
/// empty transfers.
fn royalty_info(&self, token_id: U256, sale_price: U256)
-> (Address, U256);
}

impl IErc721Royalty for Erc721Royalty {
fn royalty_info(&self, token_id: U256, sale_price: U256) -> (Address, U256) {
fn royalty_info(
&self,
token_id: U256,
sale_price: U256,
) -> (Address, U256) {
let _royalty_info = self._token_royalty_info.get(token_id);
let mut royalty_receiver = &_royalty_info.receiver;
let mut royalty_receiver = &_royalty_info.receiver;
let mut royalty_fraction = &_royalty_info.royalty_fraction;

if royalty_receiver.eq(&Address::ZERO) {
Expand All @@ -103,7 +135,8 @@ impl IErc721Royalty for Erc721Royalty {

// Check whether dereferencing impacts anything
// TODO: Check
let royalty_amount = (sale_price * U256::from(**royalty_fraction)).wrapping_div(U256::from(self._fee_denominator()));
let royalty_amount = (sale_price * U256::from(**royalty_fraction))
.wrapping_div(U256::from(self._fee_denominator()));

(**royalty_receiver, royalty_amount)
}
Expand All @@ -117,26 +150,56 @@ impl IErc165 for Erc721Royalty {
}

impl Erc721Royalty {
//? &self or &mut self ??

/// Function to change the denominator with which to interpret the fee set
/// in _setTokenRoyalty and _setDefaultRoyalty as a fraction of the sale
/// price Defaults to 10000 so fees are expressed in basis points, but
/// may be customized by an override.
///
/// # Arguments
///
/// * `&self` - Read access to the contract's state.
pub fn _fee_denominator(&self) -> U96 {
return U96::from(10000);
U96::from(10000)
}

pub fn _set_default_royalty(&mut self, receiver: Address, fee_numerator: U96) -> Result<(), Error>{
/// Function to set the royalty information that all ids in this contract
/// will default to.
///
/// # Arguments
///
/// * `&mut self` - Write access to the contract's state.
/// * `receiver` - Address to receive the royalty.
/// * `fee_numerator` - Fraction of royalty to be given to receiver
///
/// # Errors
///
/// If `fee_numerator` > denominator, then the error
/// [`Error::InvalidDefaultRoyalty`] is returned.
///
/// If receiver is the zero address, then the error
/// [`Error::InvalidDefaultRoyaltyReceiver`] is returned.
pub fn _set_default_royalty(
&mut self,
receiver: Address,
fee_numerator: U96,
) -> Result<(), Error> {
let denominator: U256 = U256::from(self._fee_denominator());

if U256::from(fee_numerator) > denominator {
return Err(Error::InvalidDefaultRoyalty(ERC2981InvalidDefaultRoyalty{
numerator: U256::from(fee_numerator),
denominator: denominator,
}));
return Err(Error::InvalidDefaultRoyalty(
ERC2981InvalidDefaultRoyalty {
numerator: U256::from(fee_numerator),
denominator,
},
));
}

if receiver == Address::ZERO {
return Err(Error::InvalidDefaultRoyaltyReceiver(ERC2981InvalidDefaultRoyaltyReceiver{
receiver: Address::ZERO,
}));
return Err(Error::InvalidDefaultRoyaltyReceiver(
ERC2981InvalidDefaultRoyaltyReceiver {
receiver: Address::ZERO,
},
));
}

self._default_royalty_info.receiver.set(receiver);
Expand All @@ -145,37 +208,76 @@ impl Erc721Royalty {
Ok(())
}

/// Function to remove default royalty information.
///
/// # Arguments
///
/// * `&mut self` - Write access to the contract's state.
pub fn _delete_default_royalty(&mut self) {
self._default_royalty_info.receiver.set(Address::ZERO);
self._default_royalty_info.royalty_fraction.set(U96::from(0));
}

pub fn _set_token_royalty(&mut self, token_id: U256, receiver: Address, fee_numerator: U96) -> Result<(), Error>{
let denominator:U256 = U256::from(self._fee_denominator());
/// Function to set the royalty information for a specific token id,
/// overriding the global default.
///
/// # Arguments
///
/// * `&mut self` - Write access to the contract's state.
/// * `receiver` - Address to receive the royalty.
/// * `fee_numerator` - Fraction of royalty to be given to receiver
///
/// # Errors
///
/// If `fee_numerator` > denominator, then the error
/// [`Error::InvalidTokenRoyalty`] is returned.
///
/// If receiver is the zero address, then the error
/// [`Error::InvalidTokenRoyaltyReceiver`] is returned.
pub fn _set_token_royalty(
&mut self,
token_id: U256,
receiver: Address,
fee_numerator: U96,
) -> Result<(), Error> {
let denominator: U256 = U256::from(self._fee_denominator());
if U256::from(fee_numerator) > denominator {
return Err(Error::InvalidTokenRoyalty(ERC2981InvalidTokenRoyalty{
tokenId: token_id,
numerator: U256::from(fee_numerator),
denominator: denominator,
}));
return Err(Error::InvalidTokenRoyalty(
ERC2981InvalidTokenRoyalty {
tokenId: token_id,
numerator: U256::from(fee_numerator),
denominator,
},
));
}

if receiver == Address::ZERO {
return Err(Error::InvalidTokenRoyaltyReceiver(ERC2981InvalidTokenRoyaltyReceiver{
tokenId: token_id,
receiver: Address::ZERO,
}));
return Err(Error::InvalidTokenRoyaltyReceiver(
ERC2981InvalidTokenRoyaltyReceiver {
tokenId: token_id,
receiver: Address::ZERO,
},
));
}

self._token_royalty_info.setter(token_id).receiver.set(receiver);
self._token_royalty_info.setter(token_id).royalty_fraction.set(fee_numerator);
self._token_royalty_info
.setter(token_id)
.royalty_fraction
.set(fee_numerator);

Ok(())
}

pub fn _reset_token_royalty(&mut self ,token_id: U256) {
/// Function to reset royalty information for the token id back to the
/// global default.
///
/// # Arguments
///
/// * `&mut self` - Write access to the contract's state.
pub fn _reset_token_royalty(&mut self, token_id: U256) {
self._token_royalty_info.delete(token_id);
}
}

mod tests {}
mod tests {}

0 comments on commit 80d3903

Please sign in to comment.