Skip to content

Commit

Permalink
ON-490: Set roles registry per collection
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Lima committed Sep 29, 2023
1 parent 939c23e commit 3b88116
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 25 deletions.
54 changes: 40 additions & 14 deletions contracts/OriumMarketplace.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.9;

import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IRolesRegistry } from "./interfaces/IRolesRegistry.sol";
import { IERC7432 } from "./interfaces/IERC7432.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";
Expand All @@ -27,6 +27,8 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea

/// @dev rolesRegistry is a ERC-7432 contract
address public rolesRegistry;
/// @dev tokenAddress => rolesRegistry
mapping(address => address) public tokenRolesRegistry;

/// @dev deadline is set in seconds
uint256 public maxDeadline;
Expand Down Expand Up @@ -163,6 +165,12 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
address borrower
);

/**
* @param tokenAddress The NFT address.
* @param rolesRegistry The address of the roles registry.
*/
event RolesRegistrySet(address indexed tokenAddress, address indexed rolesRegistry);

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

/**
Expand Down Expand Up @@ -274,8 +282,7 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
_offer.tokenId,
_offer.lender,
msg.sender,
_expirationDate,
false
_expirationDate
);

rentals[hashRentalOffer(_offer)] = Rental({ borrower: msg.sender, expirationDate: _expirationDate });
Expand Down Expand Up @@ -370,7 +377,6 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
* @param _grantor The address of the user lending the NFT
* @param _grantee The address of the user renting the NFT
* @param _expirationDate The deadline until when the rental offer is valid
* @param _revocable If the roles are revocable or not
*/
function _batchGrantRole(
bytes32[] memory _roles,
Expand All @@ -379,9 +385,9 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
uint256 _tokenId,
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable
uint64 _expirationDate
) internal {
address _rolesRegistry = rolesRegistryOf(_tokenAddress);
for (uint256 i = 0; i < _roles.length; i++) {
_grantUniqueRoleChecked( // Needed to avoid stack too deep error
_roles[i],
Expand All @@ -390,8 +396,8 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
_grantor,
_grantee,
_expirationDate,
_revocable,
_rolesData[i]
_rolesData[i],
_rolesRegistry
);
}
}
Expand All @@ -404,7 +410,6 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
* @param _grantor The address of the user lending the NFT
* @param _grantee The address of the user renting the NFT
* @param _expirationDate The deadline until when the rental offer is valid
* @param _revocable If the roles are revocable or not
* @param _data The data for the role
*/
function _grantUniqueRoleChecked(
Expand All @@ -414,17 +419,17 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes memory _data
bytes memory _data,
address _rolesRegistry
) internal {
IRolesRegistry(rolesRegistry).grantRoleFrom(
IERC7432(_rolesRegistry).grantRoleFrom(
_role,
_tokenAddress,
_tokenId,
_grantor,
_grantee,
_expirationDate,
_revocable,
false,
_data
);
}
Expand Down Expand Up @@ -486,8 +491,9 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
address _grantor,
address _grantee
) internal {
address _rolesRegistry = rolesRegistryOf(_tokenAddress);
for (uint256 i = 0; i < _roles.length; i++) {
IRolesRegistry(rolesRegistry).revokeRoleFrom(_roles[i], _tokenAddress, _tokenId, _grantor, _grantee);
IERC7432(_rolesRegistry).revokeRoleFrom(_roles[i], _tokenAddress, _tokenId, _grantor, _grantee);
}
}

Expand Down Expand Up @@ -626,6 +632,17 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
maxDeadline = _maxDeadline;
}

/**
* @notice Sets the roles registry for a collection.
* @dev Only owner can set the roles registry for a collection.
* @param _tokenAddress The NFT address.
* @param _rolesRegistry The roles registry address.
*/
function setRolesRegistry(address _tokenAddress, address _rolesRegistry) external onlyOwner {
tokenRolesRegistry[_tokenAddress] = _rolesRegistry;
emit RolesRegistrySet(_tokenAddress, _rolesRegistry);
}

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

/**
Expand All @@ -636,4 +653,13 @@ contract OriumMarketplace is Initializable, OwnableUpgradeable, PausableUpgradea
function marketplaceFeeOf(address _tokenAddress) public view returns (uint256) {
return feeInfo[_tokenAddress].isCustomFee ? feeInfo[_tokenAddress].feePercentageInWei : DEFAULT_FEE_PERCENTAGE;
}

/**
* @notice Gets the roles registry for a collection.
* @dev If no custom roles registry is set, the default roles registry will be used.
* @param _tokenAddress The NFT address.
*/
function rolesRegistryOf(address _tokenAddress) public view returns (address) {
return tokenRolesRegistry[_tokenAddress] == address(0) ? rolesRegistry : tokenRolesRegistry[_tokenAddress];
}
}
9 changes: 0 additions & 9 deletions contracts/interfaces/IRolesRegistry.sol

This file was deleted.

47 changes: 46 additions & 1 deletion test/OriumMarketplace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,39 @@ describe('OriumMarketplace', () => {
await marketplace.connect(lender).createRentalOffer(rentalOffer)
})
describe('Accept Rental Offer', async () => {
it('Should accept a rental offer', async () => {
it('Should accept a public rental offer', async () => {
const blockTimestamp = (await ethers.provider.getBlock('latest')).timestamp
const expirationDate = blockTimestamp + duration + 1
await expect(marketplace.connect(borrower).acceptRentalOffer(rentalOffer, duration))
.to.emit(marketplace, 'RentalStarted')
.withArgs(
rentalOffer.nonce,
rentalOffer.tokenAddress,
rentalOffer.tokenId,
rentalOffer.lender,
borrower.address,
expirationDate,
)
})
it('Should accept a private rental offer', async () => {
rentalOffer.borrower = borrower.address
rentalOffer.nonce = `0x${randomBytes(32).toString('hex')}`
await marketplace.connect(lender).createRentalOffer(rentalOffer)
const blockTimestamp = (await ethers.provider.getBlock('latest')).timestamp
const expirationDate = blockTimestamp + duration + 1
await expect(marketplace.connect(borrower).acceptRentalOffer(rentalOffer, duration))
.to.emit(marketplace, 'RentalStarted')
.withArgs(
rentalOffer.nonce,
rentalOffer.tokenAddress,
rentalOffer.tokenId,
rentalOffer.lender,
borrower.address,
expirationDate,
)
})
it('Should accept a rental offer if token has a different registry', async () => {
await marketplace.connect(operator).setRolesRegistry(mockERC721.address, rolesRegistry.address)
const blockTimestamp = (await ethers.provider.getBlock('latest')).timestamp
const expirationDate = blockTimestamp + duration + 1
await expect(marketplace.connect(borrower).acceptRentalOffer(rentalOffer, duration))
Expand Down Expand Up @@ -579,6 +611,19 @@ describe('OriumMarketplace', () => {
)
})
})

describe('Roles Registry', async () => {
it('Should set the roles registry for a collection', async () => {
await expect(marketplace.connect(operator).setRolesRegistry(mockERC721.address, rolesRegistry.address))
.to.emit(marketplace, 'RolesRegistrySet')
.withArgs(mockERC721.address, rolesRegistry.address)
})
it('Should NOT set the roles registry if caller is not the operator', async () => {
await expect(
marketplace.connect(notOperator).setRolesRegistry(mockERC721.address, rolesRegistry.address),
).to.be.revertedWith('Ownable: caller is not the owner')
})
})
})
})
})
2 changes: 1 addition & 1 deletion test/fixtures/OriumMarketplaceFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Contract } from 'ethers'
export async function deployMarketplaceContracts() {
const [, operator] = await ethers.getSigners()

const rolesRegistry = await ethers.getContractAt('IRolesRegistry', RolesRegistryAddress)
const rolesRegistry = await ethers.getContractAt('IERC7432', RolesRegistryAddress)

const MarketplaceFactory = await ethers.getContractFactory('OriumMarketplace')
const marketplace = await upgrades.deployProxy(MarketplaceFactory, [
Expand Down

0 comments on commit 3b88116

Please sign in to comment.