Skip to content

Commit

Permalink
Merge pull request #51 from OriumNetwork/feature--ON-872
Browse files Browse the repository at this point in the history
ON-872: delist rental offer
  • Loading branch information
karacurt authored May 21, 2024
2 parents 21645af + 82671cf commit 123ea0c
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 2 deletions.
183 changes: 183 additions & 0 deletions .openzeppelin/polygon.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {}
}
}
}
}
4 changes: 2 additions & 2 deletions addresses/polygon/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
"OriumSftMarketplace": {
"address": "0xB1D47B09aa6D81d7B00C3A37705a6A157B83C49F",
"operator": "0x359E1208DE02Af11461A37D72165Ef2dcD2Adfc8",
"implementation": "0xd04d97d855A24b4E70D5723Bb90C194EC8946938",
"implementation": "0x28c0DFe3e6B53ff3A25B9655A0B9D62120Ae34Fc",
"proxyAdmin": "0x48c769f6a8de57d824f0e7330d7A27dee53a43cD",
"libraries": {
"LibOriumSftMarketplace": "0x69C4c9D8B8D815c57Cb252f228159b2794094a17"
"LibOriumSftMarketplace": "0xF992Cf6815aC9c08B3631b5587e16d561ef9a969"
}
},
"ERC7432WrapperForERC4907": {
Expand Down
36 changes: 36 additions & 0 deletions contracts/OriumSftMarketplace.sol
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,26 @@ contract OriumSftMarketplace is Initializable, OwnableUpgradeable, PausableUpgra
emit RentalOfferCancelled(_offer.lender, _offer.nonce);
}

/**
* @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 {
_delistRentalOffer(_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 {
_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.
Expand Down Expand Up @@ -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 ########### **/
Expand Down
87 changes: 87 additions & 0 deletions test/OriumSftMarketplace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 123ea0c

Please sign in to comment.