From 7574f97c792da630517d3b17c4c5518a4d534f28 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Fri, 25 Oct 2024 13:27:52 +0300 Subject: [PATCH] add min and max price fro the bid price per second --- .../contracts/diamond/facets/Marketplace.sol | 31 ++++++++++++++++++- .../diamond/storages/MarketplaceStorage.sol | 6 ++++ .../interfaces/facets/IMarketplace.sol | 19 +++++++++++- .../storage/IMarketplaceStorage.sol | 7 +++++ .../test/diamond/facets/Marketplace.test.ts | 31 +++++++++++++++++-- .../test/diamond/facets/ModelRegistry.test.ts | 2 +- .../diamond/facets/ProviderRegistry.test.ts | 2 +- .../test/diamond/facets/SessionRouter.test.ts | 2 +- .../deployers/diamond/facets/marketplace.ts | 9 ++++-- 9 files changed, 100 insertions(+), 9 deletions(-) diff --git a/smart-contracts/contracts/diamond/facets/Marketplace.sol b/smart-contracts/contracts/diamond/facets/Marketplace.sol index c3e20659..81be7d44 100644 --- a/smart-contracts/contracts/diamond/facets/Marketplace.sol +++ b/smart-contracts/contracts/diamond/facets/Marketplace.sol @@ -24,9 +24,15 @@ contract Marketplace is using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.Bytes32Set; - function __Marketplace_init(address token_) external initializer(BIDS_STORAGE_SLOT) { + function __Marketplace_init( + address token_, + uint256 bidMinPricePerSecond_, + uint256 bidMaxPricePerSecond_ + ) external initializer(BIDS_STORAGE_SLOT) { BidsStorage storage bidsStorage = getBidsStorage(); bidsStorage.token = token_; + + setMinMaxBidPricePerSecond(bidMinPricePerSecond_, bidMaxPricePerSecond_); } function setMarketplaceBidFee(uint256 bidFee_) external onlyOwner { @@ -36,6 +42,25 @@ contract Marketplace is emit MaretplaceFeeUpdated(bidFee_); } + function setMinMaxBidPricePerSecond( + uint256 bidMinPricePerSecond_, + uint256 bidMaxPricePerSecond_ + ) public onlyOwner { + if (bidMinPricePerSecond_ == 0) { + revert MarketplaceBidMinPricePerSecondIsZero(); + } + + if (bidMinPricePerSecond_ > bidMaxPricePerSecond_) { + revert MarketplaceBidMinPricePerSecondIsInvalid(); + } + + MarketStorage storage marketStorage = getMarketStorage(); + marketStorage.bidMinPricePerSecond = bidMinPricePerSecond_; + marketStorage.bidMaxPricePerSecond = bidMaxPricePerSecond_; + + emit MarketplaceBidMinMaxPriceUpdated(bidMinPricePerSecond_, bidMaxPricePerSecond_); + } + function postModelBid(bytes32 modelId_, uint256 pricePerSecond_) external returns (bytes32 bidId) { address provider_ = _msgSender(); @@ -49,6 +74,10 @@ contract Marketplace is BidsStorage storage bidsStorage = getBidsStorage(); MarketStorage storage marketStorage = getMarketStorage(); + if (pricePerSecond_ < marketStorage.bidMinPricePerSecond || pricePerSecond_ > marketStorage.bidMaxPricePerSecond) { + revert MarketplaceBidPricePerSecondInvalid(); + } + IERC20(bidsStorage.token).safeTransferFrom(_msgSender(), address(this), marketStorage.bidFee); marketStorage.feeBalance += marketStorage.bidFee; diff --git a/smart-contracts/contracts/diamond/storages/MarketplaceStorage.sol b/smart-contracts/contracts/diamond/storages/MarketplaceStorage.sol index d905fada..91ad4158 100644 --- a/smart-contracts/contracts/diamond/storages/MarketplaceStorage.sol +++ b/smart-contracts/contracts/diamond/storages/MarketplaceStorage.sol @@ -7,6 +7,8 @@ contract MarketplaceStorage is IMarketplaceStorage { struct MarketStorage { uint256 feeBalance; // Total fees balance of the contract uint256 bidFee; + uint256 bidMinPricePerSecond; + uint256 bidMaxPricePerSecond; } bytes32 public constant MARKET_STORAGE_SLOT = keccak256("diamond.standard.market.storage"); @@ -20,6 +22,10 @@ contract MarketplaceStorage is IMarketplaceStorage { return getMarketStorage().feeBalance; } + function getMinMaxBidPricePerSecond() external view returns (uint256, uint256) { + return (getMarketStorage().bidMinPricePerSecond, getMarketStorage().bidMaxPricePerSecond); + } + /** INTERNAL */ function getMarketStorage() internal pure returns (MarketStorage storage ds) { bytes32 slot_ = MARKET_STORAGE_SLOT; diff --git a/smart-contracts/contracts/interfaces/facets/IMarketplace.sol b/smart-contracts/contracts/interfaces/facets/IMarketplace.sol index e24723d6..04b72b7d 100644 --- a/smart-contracts/contracts/interfaces/facets/IMarketplace.sol +++ b/smart-contracts/contracts/interfaces/facets/IMarketplace.sol @@ -7,15 +7,22 @@ interface IMarketplace is IMarketplaceStorage { event MaretplaceFeeUpdated(uint256 bidFee); event MarketplaceBidPosted(address indexed provider, bytes32 indexed modelId, uint256 nonce); event MarketplaceBidDeleted(address indexed provider, bytes32 indexed modelId, uint256 nonce); + event MarketplaceBidMinMaxPriceUpdated(uint256 bidMinPricePerSecond, uint256 bidMaxPricePerSecond); + error MarketplaceProviderNotFound(); error MarketplaceModelNotFound(); error MarketplaceActiveBidNotFound(); + error MarketplaceBidMinPricePerSecondIsZero(); + error MarketplaceBidMinPricePerSecondIsInvalid(); + error MarketplaceBidPricePerSecondInvalid(); /** * The function to initialize the facet. * @param token_ Stake token (MOR) + * @param bidMinPricePerSecond_ Min price per second for bid + * @param bidMaxPricePerSecond_ Max price per second for bid */ - function __Marketplace_init(address token_) external; + function __Marketplace_init(address token_, uint256 bidMinPricePerSecond_, uint256 bidMaxPricePerSecond_) external; /** * The function to set the bidFee. @@ -23,6 +30,16 @@ interface IMarketplace is IMarketplaceStorage { */ function setMarketplaceBidFee(uint256 bidFee_) external; + /** + * The function to set the min and max price per second for bid. + * @param bidMinPricePerSecond_ Min price per second for bid + * @param bidMaxPricePerSecond_ Max price per second for bid + */ + function setMinMaxBidPricePerSecond( + uint256 bidMinPricePerSecond_, + uint256 bidMaxPricePerSecond_ + ) external; + /** * The function to create the bid. * @param modelId_ The mode ID diff --git a/smart-contracts/contracts/interfaces/storage/IMarketplaceStorage.sol b/smart-contracts/contracts/interfaces/storage/IMarketplaceStorage.sol index 36ca7c70..bbf05153 100644 --- a/smart-contracts/contracts/interfaces/storage/IMarketplaceStorage.sol +++ b/smart-contracts/contracts/interfaces/storage/IMarketplaceStorage.sol @@ -11,4 +11,11 @@ interface IMarketplaceStorage { * The function returns fee balance. */ function getFeeBalance() external view returns (uint256); + + /** + * The function returns min and max price per second for bid. + * @return Min bid price per second + * @return Max bid price per second + */ + function getMinMaxBidPricePerSecond() external view returns (uint256, uint256); } diff --git a/smart-contracts/test/diamond/facets/Marketplace.test.ts b/smart-contracts/test/diamond/facets/Marketplace.test.ts index 04172e7c..90762b05 100644 --- a/smart-contracts/test/diamond/facets/Marketplace.test.ts +++ b/smart-contracts/test/diamond/facets/Marketplace.test.ts @@ -41,7 +41,7 @@ describe('Marketplace', () => { deployFacetProviderRegistry(diamond), deployFacetModelRegistry(diamond), deployFacetSessionRouter(diamond, OWNER), - deployFacetMarketplace(diamond, token), + deployFacetMarketplace(diamond, token, wei(0.0001), wei(900)), ]); await token.transfer(SECOND, wei(1000)); @@ -67,7 +67,7 @@ describe('Marketplace', () => { expect(await marketplace.getToken()).to.eq(await token.getAddress()); }); it('should revert if try to call init function twice', async () => { - await expect(marketplace.__Marketplace_init(token)).to.be.rejectedWith( + await expect(marketplace.__Marketplace_init(token, wei(0.001), wei(0.002))).to.be.rejectedWith( 'Initializable: contract is already initialized', ); }); @@ -89,6 +89,33 @@ describe('Marketplace', () => { }); }); + describe('#setMinMaxBidPricePerSecond', async () => { + it('should set min and max price per second', async () => { + await expect(marketplace.setMinMaxBidPricePerSecond(wei(1), wei(2))) + .to.emit(marketplace, 'MarketplaceBidMinMaxPriceUpdated') + .withArgs(wei(1), wei(2)); + + expect(await marketplace.getMinMaxBidPricePerSecond()).deep.eq([wei(1), wei(2)]); + }); + it('should throw error when caller is not an owner', async () => { + await expect( + marketplace.connect(SECOND).setMinMaxBidPricePerSecond(wei(1), wei(2)), + ).to.be.revertedWithCustomError(diamond, 'OwnableUnauthorizedAccount'); + }); + it('should throw error when min price is zero', async () => { + await expect(marketplace.setMinMaxBidPricePerSecond(wei(0), wei(2))).to.be.revertedWithCustomError( + marketplace, + 'MarketplaceBidMinPricePerSecondIsZero', + ); + }); + it('should throw error when min price greater then max price', async () => { + await expect(marketplace.setMinMaxBidPricePerSecond(wei(3), wei(2))).to.be.revertedWithCustomError( + marketplace, + 'MarketplaceBidMinPricePerSecondIsInvalid', + ); + }); + }); + describe('#postModelBid', async () => { beforeEach(async () => { await marketplace.setMarketplaceBidFee(wei(1)); diff --git a/smart-contracts/test/diamond/facets/ModelRegistry.test.ts b/smart-contracts/test/diamond/facets/ModelRegistry.test.ts index 01fe0f26..04bddbc0 100644 --- a/smart-contracts/test/diamond/facets/ModelRegistry.test.ts +++ b/smart-contracts/test/diamond/facets/ModelRegistry.test.ts @@ -40,7 +40,7 @@ describe('ModelRegistry', () => { deployFacetProviderRegistry(diamond), deployFacetModelRegistry(diamond), deployFacetSessionRouter(diamond, OWNER), - deployFacetMarketplace(diamond, token), + deployFacetMarketplace(diamond, token, wei(0.0001), wei(900)), ]); await token.transfer(SECOND, wei(1000)); diff --git a/smart-contracts/test/diamond/facets/ProviderRegistry.test.ts b/smart-contracts/test/diamond/facets/ProviderRegistry.test.ts index bfc82b92..9a392db2 100644 --- a/smart-contracts/test/diamond/facets/ProviderRegistry.test.ts +++ b/smart-contracts/test/diamond/facets/ProviderRegistry.test.ts @@ -41,7 +41,7 @@ describe('ProviderRegistry', () => { deployFacetProviderRegistry(diamond), deployFacetModelRegistry(diamond), deployFacetSessionRouter(diamond, OWNER), - deployFacetMarketplace(diamond, token), + deployFacetMarketplace(diamond, token, wei(0.0001), wei(900)), ]); await token.transfer(PROVIDER, wei(1000)); diff --git a/smart-contracts/test/diamond/facets/SessionRouter.test.ts b/smart-contracts/test/diamond/facets/SessionRouter.test.ts index d5b6f0da..cb994f01 100644 --- a/smart-contracts/test/diamond/facets/SessionRouter.test.ts +++ b/smart-contracts/test/diamond/facets/SessionRouter.test.ts @@ -47,7 +47,7 @@ describe('SessionRouter', () => { deployFacetProviderRegistry(diamond), deployFacetModelRegistry(diamond), deployFacetSessionRouter(diamond, FUNDING), - deployFacetMarketplace(diamond, token), + deployFacetMarketplace(diamond, token, wei(0.0001), wei(900)), ]); await token.transfer(SECOND, wei(10000)); diff --git a/smart-contracts/test/helpers/deployers/diamond/facets/marketplace.ts b/smart-contracts/test/helpers/deployers/diamond/facets/marketplace.ts index 49ab75e6..33229561 100644 --- a/smart-contracts/test/helpers/deployers/diamond/facets/marketplace.ts +++ b/smart-contracts/test/helpers/deployers/diamond/facets/marketplace.ts @@ -10,7 +10,12 @@ import { } from '@/generated-types/ethers'; import { FacetAction } from '@/test/helpers/deployers/diamond/lumerin-diamond'; -export const deployFacetMarketplace = async (diamond: LumerinDiamond, token: MorpheusToken): Promise => { +export const deployFacetMarketplace = async ( + diamond: LumerinDiamond, + token: MorpheusToken, + bidMinPrice: bigint, + bidMaxPrice: bigint, +): Promise => { let facet: Marketplace; const factory = await ethers.getContractFactory('Marketplace'); @@ -34,7 +39,7 @@ export const deployFacetMarketplace = async (diamond: LumerinDiamond, token: Mor ]); facet = facet.attach(diamond.target) as Marketplace; - await facet.__Marketplace_init(token); + await facet.__Marketplace_init(token, bidMinPrice, bidMaxPrice); return facet; };