From 594b381aa223359f41c920a789735d7a549b2427 Mon Sep 17 00:00:00 2001 From: Daniel Lima Date: Mon, 20 May 2024 10:35:12 -0300 Subject: [PATCH 1/5] ON-872: delist rental offer --- contracts/OriumSftMarketplace.sol | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/contracts/OriumSftMarketplace.sol b/contracts/OriumSftMarketplace.sol index a7de715..5fdabdc 100644 --- a/contracts/OriumSftMarketplace.sol +++ b/contracts/OriumSftMarketplace.sol @@ -198,7 +198,7 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra emit RentalStarted(_offer.lender, _offer.nonce, msg.sender, _expirationDate); } - /** + /** * @notice Cancels a rental offer. * @param _offer The rental offer struct. It should be the same as the one used to create the offer. */ @@ -228,6 +228,26 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra emit RentalOfferCancelled(_offer.lender, _offer.nonce); } + /** + * @notice Cancels a rental offer. + * @param _offer The rental offer struct. It should be the same as the one used to create the offer. + */ + function delistRentalOffer(RentalOffer calldata _offer) external whenNotPaused { + _delistRentalOffer(_offer); + } + + /** + * @notice Cancels a rental offer. + * @param _offer The rental offer struct. It should be the same as the one used to create the offer. + */ + function delistRentalOfferAndWithdraw(RentalOffer calldata _offer) external whenNotPaused { + _delistRentalOffer(_offer); + IERC7589 _rolesRegistry = IERC7589( + IOriumMarketplaceRoyalties(oriumMarketplaceRoyalties).sftRolesRegistryOf(_offer.tokenAddress) + ); + _rolesRegistry.releaseTokens(_offer.commitmentId); + } + /** * @notice Ends the rental prematurely. * @dev Can only be called by the borrower. @@ -426,6 +446,22 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra ); } + /** + * @dev Cancels a rental offer. + * @param _offer The rental offer struct. + */ + function _delistRentalOffer(RentalOffer calldata _offer) internal { + bytes32 _offerHash = LibOriumSftMarketplace.hashRentalOffer(_offer); + require(isCreated[_offerHash], 'OriumSftMarketplace: Offer not created'); + require(msg.sender == _offer.lender, 'OriumSftMarketplace: Only lender can cancel a rental offer'); + require( + nonceDeadline[_offer.lender][_offer.nonce] > block.timestamp, + 'OriumSftMarketplace: Nonce expired or not used yet' + ); + nonceDeadline[msg.sender][_offer.nonce] = uint64(block.timestamp); + emit RentalOfferCancelled(_offer.lender, _offer.nonce); + } + /** ============================ Core Functions ================================== **/ /** ######### Setters ########### **/ From ff43b640c24dab6c0f3ce711923df876b428588f Mon Sep 17 00:00:00 2001 From: Daniel Lima Date: Mon, 20 May 2024 12:45:44 -0300 Subject: [PATCH 2/5] ON-872: update comments --- contracts/OriumSftMarketplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OriumSftMarketplace.sol b/contracts/OriumSftMarketplace.sol index 5fdabdc..0891493 100644 --- a/contracts/OriumSftMarketplace.sol +++ b/contracts/OriumSftMarketplace.sol @@ -198,7 +198,7 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra emit RentalStarted(_offer.lender, _offer.nonce, msg.sender, _expirationDate); } - /** + /** * @notice Cancels a rental offer. * @param _offer The rental offer struct. It should be the same as the one used to create the offer. */ From 581d33156f6b99da0a6dd29d870b11e4c4db9414 Mon Sep 17 00:00:00 2001 From: Daniel Lima Date: Mon, 20 May 2024 12:47:48 -0300 Subject: [PATCH 3/5] ON-872: update comments --- contracts/OriumSftMarketplace.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/OriumSftMarketplace.sol b/contracts/OriumSftMarketplace.sol index 0891493..44a1c13 100644 --- a/contracts/OriumSftMarketplace.sol +++ b/contracts/OriumSftMarketplace.sol @@ -229,7 +229,7 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra } /** - * @notice Cancels a rental offer. + * @notice Delist a rental offer. * @param _offer The rental offer struct. It should be the same as the one used to create the offer. */ function delistRentalOffer(RentalOffer calldata _offer) external whenNotPaused { @@ -237,7 +237,7 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra } /** - * @notice Cancels a rental offer. + * @notice Delist a rental offer and withdraw it. * @param _offer The rental offer struct. It should be the same as the one used to create the offer. */ function delistRentalOfferAndWithdraw(RentalOffer calldata _offer) external whenNotPaused { From 39d3f2dcc55253a71f4a535a1733cb649280d44d Mon Sep 17 00:00:00 2001 From: Daniel Lima Date: Mon, 20 May 2024 20:58:29 -0300 Subject: [PATCH 4/5] ON-872: Add tests --- test/OriumSftMarketplace.test.ts | 87 ++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/test/OriumSftMarketplace.test.ts b/test/OriumSftMarketplace.test.ts index 2e30866..0943384 100644 --- a/test/OriumSftMarketplace.test.ts +++ b/test/OriumSftMarketplace.test.ts @@ -631,6 +631,93 @@ describe('OriumSftMarketplace', () => { }) }) + describe('Delist Rental Offer and Withdraw', async () => { + it('Should delist a rental offer and releaseTokens from rolesRegistry', async () => { + await expect(marketplace.connect(lender).delistRentalOfferAndWithdraw(rentalOffer)) + .to.emit(marketplace, 'RentalOfferCancelled') + .withArgs(rentalOffer.lender, rentalOffer.nonce) + .to.emit(rolesRegistry, 'TokensReleased') + .withArgs(rentalOffer.commitmentId) + }) + it('Should NOT delist a rental offer if tokens was released before directly from registry', async () => { + await rolesRegistry.connect(lender).releaseTokens(rentalOffer.commitmentId) + await expect(marketplace.connect(lender).delistRentalOfferAndWithdraw(rentalOffer)).to.be.revertedWith( + 'SftRolesRegistry: account not approved', + ) + }) + it('Should NOT delist a rental offer if contract is paused', async () => { + await marketplace.connect(operator).pause() + await expect(marketplace.connect(borrower).delistRentalOfferAndWithdraw(rentalOffer)).to.be.revertedWith( + 'Pausable: paused', + ) + }) + it('Should NOT delist a rental offer if nonce not used yet by caller', async () => { + await expect( + marketplace.connect(notOperator).delistRentalOfferAndWithdraw(rentalOffer), + ).to.be.revertedWith('OriumSftMarketplace: Only lender can cancel a rental offer') + }) + it("Should NOT delist a rental offer after deadline's expiration", async () => { + // move forward in time to expire the offer + const blockTimestamp = (await ethers.provider.getBlock('latest'))?.timestamp + const timeToMove = rentalOffer.deadline - Number(blockTimestamp) + 1 + await ethers.provider.send('evm_increaseTime', [timeToMove]) + + await expect(marketplace.connect(lender).delistRentalOfferAndWithdraw(rentalOffer)).to.be.revertedWith( + 'OriumSftMarketplace: Nonce expired or not used yet', + ) + }) + it("Should NOT delist a rental offer if it's not created", async () => { + await expect( + marketplace + .connect(lender) + .delistRentalOfferAndWithdraw({ ...rentalOffer, nonce: `0x${randomBytes(32).toString('hex')}` }), + ).to.be.revertedWith('OriumSftMarketplace: Offer not created') + }) + }) + + describe('Delist Rental Offer', async () => { + it('Should delist a rental offer and releaseTokens from rolesRegistry', async () => { + await expect(marketplace.connect(lender).delistRentalOffer(rentalOffer)) + .to.emit(marketplace, 'RentalOfferCancelled') + .withArgs(rentalOffer.lender, rentalOffer.nonce) + .to.not.emit(rolesRegistry, 'TokensReleased') + }) + it('Should delist a rental offer if tokens was released before directly from registry', async () => { + await rolesRegistry.connect(lender).releaseTokens(rentalOffer.commitmentId) + await expect(marketplace.connect(lender).delistRentalOffer(rentalOffer)) + .to.emit(marketplace, 'RentalOfferCancelled') + .withArgs(rentalOffer.lender, rentalOffer.nonce) + }) + it('Should NOT delist a rental offer if contract is paused', async () => { + await marketplace.connect(operator).pause() + await expect(marketplace.connect(borrower).delistRentalOffer(rentalOffer)).to.be.revertedWith( + 'Pausable: paused', + ) + }) + it('Should NOT delist a rental offer if nonce not used yet by caller', async () => { + await expect(marketplace.connect(notOperator).delistRentalOffer(rentalOffer)).to.be.revertedWith( + 'OriumSftMarketplace: Only lender can cancel a rental offer', + ) + }) + it("Should NOT delist a rental offer after deadline's expiration", async () => { + // move forward in time to expire the offer + const blockTimestamp = (await ethers.provider.getBlock('latest'))?.timestamp + const timeToMove = rentalOffer.deadline - Number(blockTimestamp) + 1 + await ethers.provider.send('evm_increaseTime', [timeToMove]) + + await expect(marketplace.connect(lender).delistRentalOffer(rentalOffer)).to.be.revertedWith( + 'OriumSftMarketplace: Nonce expired or not used yet', + ) + }) + it("Should NOT delist a rental offer if it's not created", async () => { + await expect( + marketplace + .connect(lender) + .delistRentalOffer({ ...rentalOffer, nonce: `0x${randomBytes(32).toString('hex')}` }), + ).to.be.revertedWith('OriumSftMarketplace: Offer not created') + }) + }) + describe('Batch Release Tokens', async () => { it('Should release tokens from rolesRegistry', async () => { await time.increase(ONE_DAY) From 82671cf5e16a0dd67299540cc1e6bd1ff70bf0f9 Mon Sep 17 00:00:00 2001 From: Daniel Lima Date: Tue, 21 May 2024 10:24:15 -0300 Subject: [PATCH 5/5] ON-872: deploy network files --- .openzeppelin/polygon.json | 183 +++++++++++++++++++++++++++++++++++ addresses/polygon/index.json | 4 +- 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/.openzeppelin/polygon.json b/.openzeppelin/polygon.json index d993b62..023739c 100644 --- a/.openzeppelin/polygon.json +++ b/.openzeppelin/polygon.json @@ -3664,6 +3664,189 @@ }, "namespaces": {} } + }, + "1f246305d6ec0d1bd26f5b4e4dd2ede7f80752e3687b10f0406bda163f6efff0": { + "address": "0x39F2fF9D3E4A403B617a8f05DE61d3C8081e857b", + "txHash": "0x54e5c816b3fdfb7ff02cb9cc978f9cf5e24311a316d5f63e89f931e5093e98e5", + "layout": { + "solcVersion": "0.8.9", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_paused", + "offset": 0, + "slot": "101", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "oriumMarketplaceRoyalties", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OriumSftMarketplace", + "src": "contracts/OriumSftMarketplace.sol:22" + }, + { + "label": "isCreated", + "offset": 0, + "slot": "152", + "type": "t_mapping(t_bytes32,t_bool)", + "contract": "OriumSftMarketplace", + "src": "contracts/OriumSftMarketplace.sol:25" + }, + { + "label": "nonceDeadline", + "offset": 0, + "slot": "153", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint64))", + "contract": "OriumSftMarketplace", + "src": "contracts/OriumSftMarketplace.sol:28" + }, + { + "label": "commitmentIdToNonce", + "offset": 0, + "slot": "154", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))", + "contract": "OriumSftMarketplace", + "src": "contracts/OriumSftMarketplace.sol:31" + }, + { + "label": "rentals", + "offset": 0, + "slot": "155", + "type": "t_mapping(t_bytes32,t_struct(Rental)1080_storage)", + "contract": "OriumSftMarketplace", + "src": "contracts/OriumSftMarketplace.sol:34" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "label": "mapping(address => mapping(uint256 => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint64))": { + "label": "mapping(address => mapping(uint256 => uint64))", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_bool)": { + "label": "mapping(bytes32 => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(Rental)1080_storage)": { + "label": "mapping(bytes32 => struct OriumSftMarketplace.Rental)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint64)": { + "label": "mapping(uint256 => uint64)", + "numberOfBytes": "32" + }, + "t_struct(Rental)1080_storage": { + "label": "struct OriumSftMarketplace.Rental", + "members": [ + { + "label": "borrower", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "expirationDate", + "type": "t_uint64", + "offset": 20, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } } } } diff --git a/addresses/polygon/index.json b/addresses/polygon/index.json index 64dd92a..ee2fb69 100644 --- a/addresses/polygon/index.json +++ b/addresses/polygon/index.json @@ -29,10 +29,10 @@ "OriumSftMarketplace": { "address": "0xB1D47B09aa6D81d7B00C3A37705a6A157B83C49F", "operator": "0x359E1208DE02Af11461A37D72165Ef2dcD2Adfc8", - "implementation": "0xd04d97d855A24b4E70D5723Bb90C194EC8946938", + "implementation": "0x28c0DFe3e6B53ff3A25B9655A0B9D62120Ae34Fc", "proxyAdmin": "0x48c769f6a8de57d824f0e7330d7A27dee53a43cD", "libraries": { - "LibOriumSftMarketplace": "0x69C4c9D8B8D815c57Cb252f228159b2794094a17" + "LibOriumSftMarketplace": "0xF992Cf6815aC9c08B3631b5587e16d561ef9a969" } }, "ERC7432WrapperForERC4907": {