Skip to content

Commit

Permalink
ON-813: ERC721 Marketplace - Accept Rental Offer
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Lima committed Apr 17, 2024
1 parent db7eb51 commit 977d25c
Show file tree
Hide file tree
Showing 11 changed files with 829 additions and 1,843 deletions.
823 changes: 0 additions & 823 deletions contracts/OriumMarketplace.sol

This file was deleted.

118 changes: 59 additions & 59 deletions contracts/OriumNftMarketplace.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
pragma solidity 0.8.9;

import { IERC721 } from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { IOriumMarketplaceRoyalties } from './interfaces/IOriumMarketplaceRoyalties.sol';
import { OwnableUpgradeable } from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import { Initializable } from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import { PausableUpgradeable } from '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol';
import { LibOriumNftMarketplace, RentalOffer, Rental } from './libraries/LibOriumNftMarketplace.sol';

/**
* @title Orium NFT Marketplace - Marketplace for renting NFTs
Expand All @@ -28,22 +30,8 @@ contract OriumNftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra
/// @dev role => tokenAddress => tokenId => deadline
mapping(bytes32 => mapping(address => mapping(uint256 => uint64))) public roleDeadline;

/** ######### Structs ########### **/

/// @dev Rental offer info.
struct RentalOffer {
address lender;
address borrower;
address tokenAddress;
uint256 tokenId;
address feeTokenAddress;
uint256 feeAmountPerSecond;
uint256 nonce;
uint64 deadline;
uint64 minDuration;
bytes32[] roles;
bytes[] rolesData;
}
/// @dev hashedOffer => Rental
mapping(bytes32 => Rental) public rentals;

/** ######### Events ########### **/

Expand Down Expand Up @@ -73,6 +61,14 @@ contract OriumNftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra
bytes[] rolesData
);

/**
* @param lender The address of the lender
* @param nonce The nonce of the rental offer
* @param borrower The address of the borrower
* @param expirationDate The expiration date of the rental
*/
event RentalStarted(address indexed lender, uint256 indexed nonce, address indexed borrower, uint64 expirationDate);

/** ######### Modifiers ########### **/

/**
Expand Down Expand Up @@ -116,9 +112,13 @@ contract OriumNftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra
function createRentalOffer(
RentalOffer calldata _offer
) external onlyTokenOwner(_offer.tokenAddress, _offer.tokenId) {
_validateCreateRentalOffer(_offer);
LibOriumNftMarketplace.validateCreateRentalOfferParams(
oriumMarketplaceRoyalties,
_offer,
nonceDeadline[_offer.lender][_offer.nonce]
);

bytes32 _offerHash = hashRentalOffer(_offer);
bytes32 _offerHash = LibOriumNftMarketplace.hashRentalOffer(_offer);

nonceDeadline[msg.sender][_offer.nonce] = _offer.deadline;
isCreated[_offerHash] = true;
Expand All @@ -145,14 +145,49 @@ contract OriumNftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra
);
}

/** ######### Getters ########### **/

/**
* @notice Gets the rental offer hash.
* @param _offer The rental offer struct to be hashed.
* @notice Accepts a rental offer.
* @dev The borrower can be address(0) to allow anyone to rent the NFT.
* @param _offer The rental offer struct. It should be the same as the one used to create the offer.
* @param _duration The duration of the rental.
*/
function hashRentalOffer(RentalOffer memory _offer) public pure returns (bytes32) {
return keccak256(abi.encode(_offer));
function acceptRentalOffer(RentalOffer calldata _offer, uint64 _duration) external whenNotPaused {
bytes32 _offerHash = LibOriumNftMarketplace.hashRentalOffer(_offer);
uint64 _expirationDate = uint64(block.timestamp + _duration);

LibOriumNftMarketplace.validateAcceptRentalOfferParams(
_offer.borrower,
_offer.minDuration,
isCreated[_offerHash],
rentals[_offerHash].expirationDate,
_duration,
nonceDeadline[_offer.lender][_offer.nonce],
_expirationDate
);

LibOriumNftMarketplace.transferFees(
_offer.feeTokenAddress,
owner(),
_offer.lender,
oriumMarketplaceRoyalties,
_offer.tokenAddress,
_offer.feeAmountPerSecond,
_duration
);

LibOriumNftMarketplace.batchGrantRole(
oriumMarketplaceRoyalties,
_offer.tokenAddress,
_offer.tokenId,
_offer.borrower,
_expirationDate,
_offer.roles,
_offer.rolesData
);

rentals[_offerHash] = Rental({ borrower: msg.sender, expirationDate: _expirationDate });

emit RentalStarted(_offer.lender, _offer.nonce, msg.sender, _expirationDate);
}

/** ============================ Core Functions ================================== **/
Expand All @@ -174,39 +209,4 @@ contract OriumNftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra
function unpause() external onlyOwner {
_unpause();
}

/** ######### Internals ########### **/

/**
* @dev Validates the create rental offer.
* @param _offer The rental offer struct.
*/
function _validateCreateRentalOffer(RentalOffer calldata _offer) internal view {
require(
IOriumMarketplaceRoyalties(oriumMarketplaceRoyalties).isTrustedFeeTokenAddressForToken(
_offer.tokenAddress,
_offer.feeTokenAddress
),
'OriumSftMarketplace: tokenAddress is not trusted'
);
require(
_offer.deadline <= block.timestamp + IOriumMarketplaceRoyalties(oriumMarketplaceRoyalties).maxDuration() &&
_offer.deadline > block.timestamp,
'OriumNftMarketplace: Invalid deadline'
);
require(nonceDeadline[_offer.lender][_offer.nonce] == 0, 'OriumNftMarketplace: nonce already used');

require(_offer.nonce != 0, 'OriumNftMarketplace: Nonce cannot be 0');
require(msg.sender == _offer.lender, 'OriumNftMarketplace: Sender and Lender mismatch');
require(_offer.roles.length > 0, 'OriumNftMarketplace: roles should not be empty');
require(
_offer.roles.length == _offer.rolesData.length,
'OriumNftMarketplace: roles and rolesData should have the same length'
);
require(
_offer.borrower != address(0) || _offer.feeAmountPerSecond > 0,
'OriumNftMarketplace: feeAmountPerSecond should be greater than 0'
);
require(_offer.minDuration <= _offer.deadline - block.timestamp, 'OriumNftMarketplace: minDuration is invalid');
}
}
163 changes: 57 additions & 106 deletions contracts/interfaces/IERC7432.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,177 +2,128 @@

pragma solidity 0.8.9;

import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

struct RoleData {
uint64 expirationDate;
bool revocable;
bytes data;
}

struct RoleAssignment {
bytes32 role;
address tokenAddress;
uint256 tokenId;
address grantor;
address grantee;
uint64 expirationDate;
bytes data;
}
import { IERC165 } from '@openzeppelin/contracts/utils/introspection/IERC165.sol';

/// @title ERC-7432 Non-Fungible Token Roles
/// @dev See https://eips.ethereum.org/EIPS/eip-7432
/// Note: the ERC-165 identifier for this interface is 0x04984ac8.
/// Note: the ERC-165 identifier for this interface is 0xfecc9ed3.
interface IERC7432 is IERC165 {
struct Role {
bytes32 roleId;
address tokenAddress;
uint256 tokenId;
address recipient;
uint64 expirationDate;
bool revocable;
bytes data;
}

/** Events **/

/// @notice Emitted when a role is granted.
/// @param _role The role identifier.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantor The user assigning the role.
/// @param _grantee The user receiving the role.
/// @param _roleId The role identifier.
/// @param _owner The user assigning the role.
/// @param _recipient The user receiving the role.
/// @param _expirationDate The expiration date of the role.
/// @param _revocable Whether the role is revocable or not.
/// @param _data Any additional data about the role.
event RoleGranted(
bytes32 indexed _role,
address indexed _tokenAddress,
uint256 indexed _tokenId,
address _grantor,
address _grantee,
bytes32 indexed _roleId,
address _owner,
address _recipient,
uint64 _expirationDate,
bool _revocable,
bytes _data
);

/// @notice Emitted when a role is revoked.
/// @param _role The role identifier.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _revoker The user revoking the role.
/// @param _grantee The user that receives the role revocation.
event RoleRevoked(
bytes32 indexed _role,
address indexed _tokenAddress,
uint256 indexed _tokenId,
address _revoker,
address _grantee
);
/// @param _roleId The role identifier.
event RoleRevoked(address indexed _tokenAddress, uint256 indexed _tokenId, bytes32 indexed _roleId);

/// @notice Emitted when a user is approved to manage any role on behalf of another user.
/// @notice Emitted when a user is approved to manage roles on behalf of another user.
/// @param _tokenAddress The token address.
/// @param _operator The user approved to grant and revoke roles.
/// @param _isApproved The approval status.
event RoleApprovalForAll(address indexed _tokenAddress, address indexed _operator, bool _isApproved);
event RoleApprovalForAll(address indexed _tokenAddress, address indexed _operator, bool indexed _isApproved);

/** External Functions **/

/// @notice Grants a role on behalf of a user.
/// @param _roleAssignment The role assignment data.
function grantRoleFrom(RoleAssignment calldata _roleAssignment) external;

/// @notice Grants a role on behalf of a user.
/// @param _roleAssignment The role assignment data.
function grantRevocableRoleFrom(RoleAssignment calldata _roleAssignment) external;
/// @notice Grants a role to a user.
/// @param _role The role attributes.
function grantRole(Role calldata _role) external;

/// @notice Revokes a role on behalf of a user.
/// @param _role The role identifier.
/// @notice Revokes a role from a user.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _revoker The user revoking the role.
/// @param _grantee The user that receives the role revocation.
function revokeRoleFrom(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _revoker,
address _grantee
) external;
/// @param _roleId The role identifier.
function revokeRole(address _tokenAddress, uint256 _tokenId, bytes32 _roleId) external;

/// @notice Approves operator to grant and revoke any roles on behalf of another user.
/// @notice Approves operator to grant and revoke roles on behalf of another user.
/// @param _tokenAddress The token address.
/// @param _operator The user approved to grant and revoke roles.
/// @param _approved The approval status.
function setRoleApprovalForAll(address _tokenAddress, address _operator, bool _approved) external;

/** View Functions **/

/// @notice Checks if a user has a role.
/// @param _role The role identifier.
/// @notice Retrieves the recipient of an NFT role.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantor The user that assigned the role.
/// @param _grantee The user that received the role.
function hasNonUniqueRole(
bytes32 _role,
/// @param _roleId The role identifier.
/// @return recipient_ The user that received the role.
function recipientOf(
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee
) external view returns (bool);
bytes32 _roleId
) external view returns (address recipient_);

/// @notice Checks if a user has a unique role.
/// @param _role The role identifier.
/// @notice Retrieves the custom data of a role assignment.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantor The user that assigned the role.
/// @param _grantee The user that received the role.
function hasRole(
bytes32 _role,
/// @param _roleId The role identifier.
/// @return data_ The custom data of the role.
function roleData(
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee
) external view returns (bool);
bytes32 _roleId
) external view returns (bytes memory data_);

/// @notice Returns the custom data of a role assignment.
/// @param _role The role identifier.
/// @notice Retrieves the expiration date of a role assignment.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantor The user that assigned the role.
/// @param _grantee The user that received the role.
function roleData(
bytes32 _role,
/// @param _roleId The role identifier.
/// @return expirationDate_ The expiration date of the role.
function roleExpirationDate(
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee
) external view returns (RoleData memory data_);
bytes32 _roleId
) external view returns (uint64 expirationDate_);

/// @notice Returns the expiration date of a role assignment.
/// @param _role The role identifier.
/// @notice Verifies if the role is revocable.
/// @param _tokenAddress The token address.
/// @param _tokenId The token identifier.
/// @param _grantor The user that assigned the role.
/// @param _grantee The user that received the role.
function roleExpirationDate(
bytes32 _role,
/// @param _roleId The role identifier.
/// @return revocable_ Whether the role is revocable.
function isRoleRevocable(
address _tokenAddress,
uint256 _tokenId,
address _grantor,
address _grantee
) external view returns (uint64 expirationDate_);
bytes32 _roleId
) external view returns (bool revocable_);

/// @notice Checks if the grantor approved the operator for all NFTs.
/// @notice Verifies if the owner approved the operator.
/// @param _tokenAddress The token address.
/// @param _grantor The user that approved the operator.
/// @param _owner The user that approved the operator.
/// @param _operator The user that can grant and revoke roles.
/// @return Whether the operator is approved.
function isRoleApprovedForAll(
address _tokenAddress,
address _grantor,
address _owner,
address _operator
) external view returns (bool);

/// @notice Returns the last grantee of a role.
/// @param _role The role.
/// @param _tokenAddress The token address.
/// @param _tokenId The token ID.
/// @param _grantor The user that granted the role.
function lastGrantee(
bytes32 _role,
address _tokenAddress,
uint256 _tokenId,
address _grantor
) external view returns (address);
}
Loading

0 comments on commit 977d25c

Please sign in to comment.