From 88086b1b096d7dfc787f7ebf14e711b19ea447bb Mon Sep 17 00:00:00 2001 From: CanvasL <746591811@qq.com> Date: Tue, 6 Aug 2024 15:33:44 +0800 Subject: [PATCH] feat: emit events --- {src => contracts}/AccessToken.sol | 0 {src => contracts}/AccessTokenFactory.sol | 0 {src => contracts}/Marketplace.sol | 128 +++++++++++++++--- contracts/interfaces/IMarketplace.sol | 40 ++++++ contracts/interfaces/IMarketplaceEvents.sol | 61 +++++++++ .../interfaces/IMarketplaceStructs.sol | 2 +- {src => contracts}/interfaces/IProduct.sol | 0 foundry.toml | 2 +- test/AccessToken.t.sol | 4 +- test/AccessTokenFactory.t.sol | 6 +- test/Marketplace.t.sol | 62 ++++----- test/mocks/MockProduct.sol | 2 +- 12 files changed, 249 insertions(+), 58 deletions(-) rename {src => contracts}/AccessToken.sol (100%) rename {src => contracts}/AccessTokenFactory.sol (100%) rename {src => contracts}/Marketplace.sol (78%) create mode 100644 contracts/interfaces/IMarketplace.sol create mode 100644 contracts/interfaces/IMarketplaceEvents.sol rename src/interfaces/IMarketplace.sol => contracts/interfaces/IMarketplaceStructs.sol (98%) rename {src => contracts}/interfaces/IProduct.sol (100%) diff --git a/src/AccessToken.sol b/contracts/AccessToken.sol similarity index 100% rename from src/AccessToken.sol rename to contracts/AccessToken.sol diff --git a/src/AccessTokenFactory.sol b/contracts/AccessTokenFactory.sol similarity index 100% rename from src/AccessTokenFactory.sol rename to contracts/AccessTokenFactory.sol diff --git a/src/Marketplace.sol b/contracts/Marketplace.sol similarity index 78% rename from src/Marketplace.sol rename to contracts/Marketplace.sol index 8b1d6ef..2581263 100644 --- a/src/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -53,22 +53,44 @@ contract Marketplace is IMarketplace, Ownable { _feePoints = feePoints; } - function getListingInfo(address accessToken, uint256 tokenId) external view returns (ListingInfo memory) { + /** + * @inheritdoc IMarketplace + */ + function getListingInfo( + address accessToken, + uint256 tokenId + ) external view returns (ListingInfo memory) { return _listings[accessToken][tokenId]; } - function getRentalInfo(address accessToken, uint256 tokenId, address tenant) external view returns (RentalInfo memory) { + /** + * @inheritdoc IMarketplace + */ + function getRentalInfo( + address accessToken, + uint256 tokenId, + address tenant + ) external view returns (RentalInfo memory) { return _rentals[accessToken][tokenId][tenant]; } + /** + * @inheritdoc IMarketplace + */ function setFeePoints(uint256 feePoints) external onlyOwner { _feePoints = feePoints; } + /** + * @inheritdoc IMarketplace + */ function setTreasury(address payable treasury) external onlyOwner { _treasury = treasury; } + /** + * @inheritdoc IMarketplace + */ function addRentCurrencies( address[] memory rentCurrencies ) external onlyOwner { @@ -77,6 +99,9 @@ contract Marketplace is IMarketplace, Ownable { } } + /** + * @inheritdoc IMarketplace + */ function removeRentCurrencies( address[] memory rentCurrencies ) external onlyOwner { @@ -85,11 +110,12 @@ contract Marketplace is IMarketplace, Ownable { } } + /** + * @inheritdoc IMarketplace + */ function list(ListArgs memory args) public { - address accessToken = ACCESS_TOKEN_FACTORY.getAccessToken( - args.product - ); - if(accessToken == address(0)) { + address accessToken = ACCESS_TOKEN_FACTORY.getAccessToken(args.product); + if (accessToken == address(0)) { accessToken = ACCESS_TOKEN_FACTORY.createAccessToken(args.product); } require( @@ -103,7 +129,8 @@ contract Marketplace is IMarketplace, Ownable { "invalid maximum rental days" ); require( - args.rentCurrency == NATIVE_TOKEN || supportedRentCurrencies[args.rentCurrency], + args.rentCurrency == NATIVE_TOKEN || + supportedRentCurrencies[args.rentCurrency], "unsupported rent currency" ); @@ -122,20 +149,36 @@ contract Marketplace is IMarketplace, Ownable { address(this), args.tokenId ); + + emit List( + msg.sender, + args.product, + accessToken, + args.tokenId, + args.minRentalDays, + args.maxRentalDays, + args.rentCurrency, + args.dailyRent, + args.rentRecipient + ); } + /** + * @inheritdoc IMarketplace + */ function delist(DelistArgs memory args) public { - ListingInfo storage listing = _listings[args.accessToken][ - args.tokenId - ]; + ListingInfo storage listing = _listings[args.accessToken][args.tokenId]; require(listing.owner == msg.sender, "not listing owner"); listing.status = ListingStatus.Delisted; + + emit Delist(msg.sender, args.accessToken, args.tokenId); } + /** + * @inheritdoc IMarketplace + */ function relist(RelistArgs memory args) public { - ListingInfo storage listing = _listings[args.accessToken][ - args.tokenId - ]; + ListingInfo storage listing = _listings[args.accessToken][args.tokenId]; require(listing.owner == msg.sender, "not listing owner"); require(args.minRentalDays > 0, "invalid minimum rental days"); require( @@ -143,7 +186,8 @@ contract Marketplace is IMarketplace, Ownable { "invalid maximum rental days" ); require( - args.rentCurrency == NATIVE_TOKEN || supportedRentCurrencies[args.rentCurrency], + args.rentCurrency == NATIVE_TOKEN || + supportedRentCurrencies[args.rentCurrency], "unsupported rent currency" ); @@ -153,8 +197,22 @@ contract Marketplace is IMarketplace, Ownable { listing.dailyRent = args.dailyRent; listing.rentRecipient = args.rentRecipient; listing.status = ListingStatus.Listing; + + emit Relist( + msg.sender, + args.accessToken, + args.tokenId, + args.minRentalDays, + args.maxRentalDays, + args.rentCurrency, + args.dailyRent, + args.rentRecipient + ); } + /** + * @inheritdoc IMarketplace + */ function rent(RentArgs memory args) public payable { require( _rentals[args.accessToken][args.tokenId][args.tenant].status == @@ -190,12 +248,20 @@ contract Marketplace is IMarketplace, Ownable { args.prepaidRent ); // Mint access token to tenant - AccessToken(args.accessToken).mint( + AccessToken(args.accessToken).mint(args.tenant, args.tokenId); + + emit Rent( args.tenant, - args.tokenId + args.accessToken, + args.tokenId, + args.rentalDays, + args.prepaidRent ); } + /** + * @inheritdoc IMarketplace + */ function payRent(PayRentArgs memory args) public payable { ListingInfo memory listing = _listings[args.accessToken][args.tokenId]; RentalInfo storage rental = _rentals[args.accessToken][args.tokenId][ @@ -209,8 +275,18 @@ contract Marketplace is IMarketplace, Ownable { // Pay rent _payRent(listing, rental, args.rent); + + emit PayRent( + args.tenant, + args.accessToken, + args.tokenId, + args.rent + ); } + /** + * @inheritdoc IMarketplace + */ function endLease(EndLeaseArgs memory args) public { RentalInfo storage rental = _rentals[args.accessToken][args.tokenId][ args.tenant @@ -227,12 +303,20 @@ contract Marketplace is IMarketplace, Ownable { // Burn tenant's access token AccessToken(args.accessToken).burn(args.tokenId); rental.status = RentalStatus.EndedOrNotExist; + + emit EndLease( + args.tenant, + args.accessToken, + args.tokenId, + msg.sender + ); } + /** + * @inheritdoc IMarketplace + */ function withdraw(WithdrawArgs memory args) public { - ListingInfo storage listing = _listings[args.accessToken][ - args.tokenId - ]; + ListingInfo storage listing = _listings[args.accessToken][args.tokenId]; require(listing.owner == msg.sender, "not listing owner"); require( !AccessToken(args.accessToken).isExist(args.tokenId), @@ -245,6 +329,12 @@ contract Marketplace is IMarketplace, Ownable { listing.owner, args.tokenId ); + + emit Withdraw( + msg.sender, + args.accessToken, + args.tokenId + ); } function _payRent( diff --git a/contracts/interfaces/IMarketplace.sol b/contracts/interfaces/IMarketplace.sol new file mode 100644 index 0000000..cf2e99c --- /dev/null +++ b/contracts/interfaces/IMarketplace.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {IMarketplaceStructs} from "./IMarketplaceStructs.sol"; +import {IMarketplaceEvents} from "./IMarketplaceEvents.sol"; + +interface IMarketplace is IMarketplaceStructs, IMarketplaceEvents { + function getListingInfo( + address accessToken, + uint256 tokenId + ) external view returns (ListingInfo memory); + + function getRentalInfo( + address accessToken, + uint256 tokenId, + address tenant + ) external view returns (RentalInfo memory); + + function setFeePoints(uint256 feePoints) external; + + function setTreasury(address payable treasury) external; + + function addRentCurrencies(address[] memory rentCurrencies) external; + + function removeRentCurrencies(address[] memory rentCurrencies) external; + + function list(ListArgs memory args) external; + + function delist(DelistArgs memory args) external; + + function relist(RelistArgs memory args) external; + + function rent(RentArgs memory args) external payable; + + function payRent(PayRentArgs memory args) external payable; + + function endLease(EndLeaseArgs memory args) external; + + function withdraw(WithdrawArgs memory args) external; +} diff --git a/contracts/interfaces/IMarketplaceEvents.sol b/contracts/interfaces/IMarketplaceEvents.sol new file mode 100644 index 0000000..f8da2c8 --- /dev/null +++ b/contracts/interfaces/IMarketplaceEvents.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface IMarketplaceEvents { + event List( + address indexed owner, + address indexed product, + address accessToken, + uint256 indexed tokenId, + uint256 minRentalDays, + uint256 maxRentalDays, + address rentCurrency, + uint256 dailyRent, + address rentRecipient + ); + + event Delist( + address indexed owner, + address indexed accessToken, + uint256 indexed tokenId + ); + + event Relist( + address indexed owner, + address indexed accessToken, + uint256 indexed tokenId, + uint256 minRentalDays, + uint256 maxRentalDays, + address rentCurrency, + uint256 dailyRent, + address rentRecipient + ); + + event Rent( + address indexed tenant, + address indexed accessToken, + uint256 indexed tokenId, + uint256 rentalDays, + uint256 prepaidRent + ); + + event PayRent( + address indexed tenant, + address indexed accessToken, + uint256 indexed tokenId, + uint256 rent + ); + + event EndLease( + address indexed tenant, + address indexed accessToken, + uint256 indexed tokenId, + address operator + ); + + event Withdraw( + address indexed owner, + address indexed accessToken, + uint256 indexed tokenId + ); +} \ No newline at end of file diff --git a/src/interfaces/IMarketplace.sol b/contracts/interfaces/IMarketplaceStructs.sol similarity index 98% rename from src/interfaces/IMarketplace.sol rename to contracts/interfaces/IMarketplaceStructs.sol index 9d8091d..5e13df9 100644 --- a/src/interfaces/IMarketplace.sol +++ b/contracts/interfaces/IMarketplaceStructs.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -interface IMarketplace { +interface IMarketplaceStructs { // Statuses of a listing. WithdrawnOrNotExist, which is 0, is effectively the same as never listed before. enum ListingStatus { WithdrawnOrNotExist, diff --git a/src/interfaces/IProduct.sol b/contracts/interfaces/IProduct.sol similarity index 100% rename from src/interfaces/IProduct.sol rename to contracts/interfaces/IProduct.sol diff --git a/foundry.toml b/foundry.toml index d7016e5..c496598 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -src = "src" +src = "contracts" out = "out" libs = ["dependencies"] auto_detect_remappings = true diff --git a/test/AccessToken.t.sol b/test/AccessToken.t.sol index 14d3c92..ba7db42 100644 --- a/test/AccessToken.t.sol +++ b/test/AccessToken.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.24; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; -import {AccessToken} from "../src/AccessToken.sol"; -import {IProduct} from "../src/interfaces/IProduct.sol"; +import {AccessToken} from "../contracts/AccessToken.sol"; +import {IProduct} from "../contracts/interfaces/IProduct.sol"; import {MockProduct} from "./mocks/MockProduct.sol"; import "forge-std/src/Test.sol"; diff --git a/test/AccessTokenFactory.t.sol b/test/AccessTokenFactory.t.sol index c3a4eb3..4054a0e 100644 --- a/test/AccessTokenFactory.t.sol +++ b/test/AccessTokenFactory.t.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.24; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import {AccessTokenFactory} from "../src/AccessTokenFactory.sol"; -import {AccessToken} from "../src/AccessToken.sol"; -import {IProduct} from "../src/interfaces/IProduct.sol"; +import {AccessTokenFactory} from "../contracts/AccessTokenFactory.sol"; +import {AccessToken} from "../contracts/AccessToken.sol"; +import {IProduct} from "../contracts/interfaces/IProduct.sol"; import {MockProduct} from "./mocks/MockProduct.sol"; import "forge-std/src/Test.sol"; diff --git a/test/Marketplace.t.sol b/test/Marketplace.t.sol index fb45141..1e8e5de 100644 --- a/test/Marketplace.t.sol +++ b/test/Marketplace.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.24; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import {AccessTokenFactory} from "../src/AccessTokenFactory.sol"; -import {IMarketplace} from "../src/interfaces/IMarketplace.sol"; -import {Marketplace} from "../src/Marketplace.sol"; -import {AccessToken} from "../src/AccessToken.sol"; -import {IProduct} from "../src/interfaces/IProduct.sol"; +import {AccessTokenFactory} from "../contracts/AccessTokenFactory.sol"; +import {IMarketplaceStructs} from "../contracts/interfaces/IMarketplaceStructs.sol"; +import {Marketplace} from "../contracts/Marketplace.sol"; +import {AccessToken} from "../contracts/AccessToken.sol"; +import {IProduct} from "../contracts/interfaces/IProduct.sol"; import {MockProduct} from "./mocks/MockProduct.sol"; import "forge-std/src/Test.sol"; @@ -43,7 +43,7 @@ contract AccessTokenFactoryTest is Test { uint256 tokenId = product.mint(address(this)); // List the product token on the marketplace - IMarketplace.ListArgs memory args = IMarketplace.ListArgs({ + IMarketplaceStructs.ListArgs memory args = IMarketplaceStructs.ListArgs({ product: productAddress, tokenId: tokenId, minRentalDays: 1, @@ -57,7 +57,7 @@ contract AccessTokenFactoryTest is Test { marketplace.list(args); // Check that the token is listed - IMarketplace.ListingInfo memory listing = marketplace.getListingInfo( + IMarketplaceStructs.ListingInfo memory listing = marketplace.getListingInfo( address(factory.getAccessToken(productAddress)), tokenId ); @@ -68,7 +68,7 @@ contract AccessTokenFactoryTest is Test { assertEq(listing.dailyRent, 1 ether); assertEq( uint256(listing.status), - uint256(IMarketplace.ListingStatus.Listing) + uint256(IMarketplaceStructs.ListingStatus.Listing) ); } @@ -77,7 +77,7 @@ contract AccessTokenFactoryTest is Test { uint256 tokenId = product.mint(address(this)); // List the product token on the marketplace - IMarketplace.ListArgs memory listArgs = IMarketplace.ListArgs({ + IMarketplaceStructs.ListArgs memory listArgs = IMarketplaceStructs.ListArgs({ product: productAddress, tokenId: tokenId, minRentalDays: 1, @@ -91,7 +91,7 @@ contract AccessTokenFactoryTest is Test { marketplace.list(listArgs); // Delist the token - IMarketplace.DelistArgs memory delistArgs = IMarketplace.DelistArgs({ + IMarketplaceStructs.DelistArgs memory delistArgs = IMarketplaceStructs.DelistArgs({ accessToken: payable(factory.getAccessToken(productAddress)), tokenId: tokenId }); @@ -99,13 +99,13 @@ contract AccessTokenFactoryTest is Test { marketplace.delist(delistArgs); // Check that the token is delisted - IMarketplace.ListingInfo memory listing = marketplace.getListingInfo( + IMarketplaceStructs.ListingInfo memory listing = marketplace.getListingInfo( address(factory.getAccessToken(productAddress)), tokenId ); assertEq( uint256(listing.status), - uint256(IMarketplace.ListingStatus.Delisted) + uint256(IMarketplaceStructs.ListingStatus.Delisted) ); } @@ -117,7 +117,7 @@ contract AccessTokenFactoryTest is Test { vm.deal(tenant, 10 ether); // List the product token on the marketplace - IMarketplace.ListArgs memory listArgs = IMarketplace.ListArgs({ + IMarketplaceStructs.ListArgs memory listArgs = IMarketplaceStructs.ListArgs({ product: productAddress, tokenId: tokenId, minRentalDays: 1, @@ -131,7 +131,7 @@ contract AccessTokenFactoryTest is Test { marketplace.list(listArgs); // Rent the token - IMarketplace.RentArgs memory rentArgs = IMarketplace.RentArgs({ + IMarketplaceStructs.RentArgs memory rentArgs = IMarketplaceStructs.RentArgs({ accessToken: factory.getAccessToken(productAddress), tokenId: tokenId, tenant: tenant, @@ -143,7 +143,7 @@ contract AccessTokenFactoryTest is Test { marketplace.rent{value: 5 ether}(rentArgs); // Check that the token is rented - IMarketplace.RentalInfo memory rental = marketplace.getRentalInfo( + IMarketplaceStructs.RentalInfo memory rental = marketplace.getRentalInfo( address(factory.getAccessToken(productAddress)), tokenId, tenant @@ -154,7 +154,7 @@ contract AccessTokenFactoryTest is Test { assertEq(rental.dailyRent, 1 ether); assertEq( uint256(rental.status), - uint256(IMarketplace.RentalStatus.Renting) + uint256(IMarketplaceStructs.RentalStatus.Renting) ); } @@ -166,7 +166,7 @@ contract AccessTokenFactoryTest is Test { vm.deal(tenant, 20 ether); // List the product token on the marketplace - IMarketplace.ListArgs memory listArgs = IMarketplace.ListArgs({ + IMarketplaceStructs.ListArgs memory listArgs = IMarketplaceStructs.ListArgs({ product: productAddress, tokenId: tokenId, minRentalDays: 1, @@ -180,7 +180,7 @@ contract AccessTokenFactoryTest is Test { marketplace.list(listArgs); // Rent the token - IMarketplace.RentArgs memory rentArgs = IMarketplace.RentArgs({ + IMarketplaceStructs.RentArgs memory rentArgs = IMarketplaceStructs.RentArgs({ accessToken: factory.getAccessToken(productAddress), tokenId: tokenId, tenant: tenant, @@ -191,7 +191,7 @@ contract AccessTokenFactoryTest is Test { vm.prank(tenant); marketplace.rent{value: 5 ether}(rentArgs); - IMarketplace.RentalInfo memory rental = marketplace.getRentalInfo( + IMarketplaceStructs.RentalInfo memory rental = marketplace.getRentalInfo( address(factory.getAccessToken(productAddress)), tokenId, tenant @@ -203,11 +203,11 @@ contract AccessTokenFactoryTest is Test { assertEq(rental.totalPaidRent, 5 ether); assertEq( uint256(rental.status), - uint256(IMarketplace.RentalStatus.Renting) + uint256(IMarketplaceStructs.RentalStatus.Renting) ); // Pay additional rent - IMarketplace.PayRentArgs memory payRentArgs = IMarketplace.PayRentArgs({ + IMarketplaceStructs.PayRentArgs memory payRentArgs = IMarketplaceStructs.PayRentArgs({ accessToken: factory.getAccessToken(productAddress), tokenId: tokenId, tenant: tenant, @@ -235,7 +235,7 @@ contract AccessTokenFactoryTest is Test { vm.deal(tenant, 10 ether); // List the product token on the marketplace - IMarketplace.ListArgs memory listArgs = IMarketplace.ListArgs({ + IMarketplaceStructs.ListArgs memory listArgs = IMarketplaceStructs.ListArgs({ product: productAddress, tokenId: tokenId, minRentalDays: 1, @@ -249,7 +249,7 @@ contract AccessTokenFactoryTest is Test { marketplace.list(listArgs); // Rent the token - IMarketplace.RentArgs memory rentArgs = IMarketplace.RentArgs({ + IMarketplaceStructs.RentArgs memory rentArgs = IMarketplaceStructs.RentArgs({ accessToken: factory.getAccessToken(productAddress), tokenId: tokenId, tenant: tenant, @@ -264,7 +264,7 @@ contract AccessTokenFactoryTest is Test { vm.warp(block.timestamp + 6 days); // End the lease - IMarketplace.EndLeaseArgs memory endLeaseArgs = IMarketplace + IMarketplaceStructs.EndLeaseArgs memory endLeaseArgs = IMarketplaceStructs .EndLeaseArgs({ accessToken: factory.getAccessToken(productAddress), tokenId: tokenId, @@ -275,14 +275,14 @@ contract AccessTokenFactoryTest is Test { marketplace.endLease(endLeaseArgs); // Check that the lease is ended - IMarketplace.RentalInfo memory rental = marketplace.getRentalInfo( + IMarketplaceStructs.RentalInfo memory rental = marketplace.getRentalInfo( address(factory.getAccessToken(productAddress)), tokenId, tenant ); assertEq( uint256(rental.status), - uint256(IMarketplace.RentalStatus.EndedOrNotExist) + uint256(IMarketplaceStructs.RentalStatus.EndedOrNotExist) ); } @@ -291,7 +291,7 @@ contract AccessTokenFactoryTest is Test { uint256 tokenId = product.mint(address(this)); // List the product token on the marketplace - IMarketplace.ListArgs memory listArgs = IMarketplace.ListArgs({ + IMarketplaceStructs.ListArgs memory listArgs = IMarketplaceStructs.ListArgs({ product: productAddress, tokenId: tokenId, minRentalDays: 1, @@ -305,7 +305,7 @@ contract AccessTokenFactoryTest is Test { marketplace.list(listArgs); // Delist the token - IMarketplace.DelistArgs memory delistArgs = IMarketplace.DelistArgs({ + IMarketplaceStructs.DelistArgs memory delistArgs = IMarketplaceStructs.DelistArgs({ accessToken: payable(factory.getAccessToken(productAddress)), tokenId: tokenId }); @@ -313,7 +313,7 @@ contract AccessTokenFactoryTest is Test { marketplace.delist(delistArgs); // Withdraw the token - IMarketplace.WithdrawArgs memory withdrawArgs = IMarketplace + IMarketplaceStructs.WithdrawArgs memory withdrawArgs = IMarketplaceStructs .WithdrawArgs({ accessToken: factory.getAccessToken(productAddress), tokenId: tokenId @@ -322,13 +322,13 @@ contract AccessTokenFactoryTest is Test { marketplace.withdraw(withdrawArgs); // Check that the token is withdrawn - IMarketplace.ListingInfo memory listing = marketplace.getListingInfo( + IMarketplaceStructs.ListingInfo memory listing = marketplace.getListingInfo( address(factory.getAccessToken(productAddress)), tokenId ); assertEq( uint256(listing.status), - uint256(IMarketplace.ListingStatus.WithdrawnOrNotExist) + uint256(IMarketplaceStructs.ListingStatus.WithdrawnOrNotExist) ); } diff --git a/test/mocks/MockProduct.sol b/test/mocks/MockProduct.sol index d03de89..29e9ad6 100644 --- a/test/mocks/MockProduct.sol +++ b/test/mocks/MockProduct.sol @@ -5,7 +5,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {IProduct} from "../../src/interfaces/IProduct.sol"; +import {IProduct} from "../../contracts/interfaces/IProduct.sol"; contract MockProduct is Initializable,