mirror of
https://github.com/lens-protocol/core.git
synced 2026-04-22 03:02:03 -04:00
Merge branch 'misc/test-coverage' into misc/test-publishing
This commit is contained in:
@@ -311,8 +311,8 @@ contract UpgradeForkTest is BaseTest {
|
||||
publicationCollectedProfileId: profileId,
|
||||
publicationCollectedId: 1,
|
||||
collectorProfileId: profileId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerProfileId: 0,
|
||||
referrerPubId: 0,
|
||||
collectModuleData: ''
|
||||
})
|
||||
);
|
||||
@@ -321,8 +321,8 @@ contract UpgradeForkTest is BaseTest {
|
||||
publicationCollectedProfileId: profileId,
|
||||
publicationCollectedId: 2,
|
||||
collectorProfileId: profileId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerProfileId: 0,
|
||||
referrerPubId: 0,
|
||||
collectModuleData: ''
|
||||
})
|
||||
);
|
||||
@@ -331,8 +331,8 @@ contract UpgradeForkTest is BaseTest {
|
||||
publicationCollectedProfileId: profileId,
|
||||
publicationCollectedId: 3,
|
||||
collectorProfileId: profileId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerProfileId: 0,
|
||||
referrerPubId: 0,
|
||||
collectModuleData: ''
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import {Types} from 'contracts/libraries/constants/Types.sol';
|
||||
|
||||
contract ArrayHelpers {
|
||||
function testArrayHelpers() public {
|
||||
// Prevents being counted in Foundry Coverage
|
||||
@@ -11,6 +13,21 @@ contract ArrayHelpers {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _emptyAddressArray() internal pure returns (address[] memory) {
|
||||
address[] memory ret = new address[](0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _emptyBytesArray() internal pure returns (bytes[] memory) {
|
||||
bytes[] memory ret = new bytes[](0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _emptyPubTypesArray() internal pure returns (Types.PublicationType[] memory) {
|
||||
Types.PublicationType[] memory ret = new Types.PublicationType[](0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _toUint256Array(uint256 n) internal pure returns (uint256[] memory) {
|
||||
uint256[] memory ret = new uint256[](1);
|
||||
ret[0] = n;
|
||||
|
||||
@@ -0,0 +1,490 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.10;
|
||||
|
||||
import 'test/foundry/base/BaseTest.t.sol';
|
||||
import {TokenGatedReferenceModule, GateParams} from 'contracts/modules/reference/TokenGatedReferenceModule.sol';
|
||||
import {Types} from 'contracts/libraries/constants/Types.sol';
|
||||
import {ArrayHelpers} from 'test/foundry/helpers/ArrayHelpers.sol';
|
||||
import {Currency} from 'test/mocks/Currency.sol';
|
||||
import {NFT} from 'test/mocks/NFT.sol';
|
||||
|
||||
contract TokenGatedReferenceModuleBase is BaseTest {
|
||||
using stdJson for string;
|
||||
TokenGatedReferenceModule tokenGatedReferenceModule;
|
||||
|
||||
NFT nft;
|
||||
Currency currency;
|
||||
uint256 profileId;
|
||||
|
||||
event TokenGatedReferencePublicationCreated(
|
||||
uint256 indexed profileId,
|
||||
uint256 indexed pubId,
|
||||
address tokenAddress,
|
||||
uint256 minThreshold
|
||||
);
|
||||
|
||||
function setUp() public override {
|
||||
super.setUp();
|
||||
currency = new Currency();
|
||||
nft = new NFT();
|
||||
profileId = _createProfile(profileOwner);
|
||||
}
|
||||
|
||||
// Deploy & Whitelist TokenGatedReferenceModule
|
||||
constructor() TestSetup() {
|
||||
if (fork && keyExists(string(abi.encodePacked('.', forkEnv, '.TokenGatedReferenceModule')))) {
|
||||
tokenGatedReferenceModule = TokenGatedReferenceModule(
|
||||
json.readAddress(string(abi.encodePacked('.', forkEnv, '.TokenGatedReferenceModule')))
|
||||
);
|
||||
console.log('Testing against already deployed module at:', address(tokenGatedReferenceModule));
|
||||
} else {
|
||||
vm.prank(deployer);
|
||||
tokenGatedReferenceModule = new TokenGatedReferenceModule(hubProxyAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////
|
||||
// Publication Creation with TokenGatedReferenceModule
|
||||
//
|
||||
contract TokenGatedReferenceModule_Publication is TokenGatedReferenceModuleBase {
|
||||
constructor() TokenGatedReferenceModuleBase() {}
|
||||
|
||||
// Negatives
|
||||
function testCannotPostWithZeroTokenAddress() public {
|
||||
vm.expectRevert(Errors.InitParamsInvalid.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
1,
|
||||
2,
|
||||
address(3),
|
||||
abi.encode(GateParams({tokenAddress: address(0), minThreshold: 1}))
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotPostWithZeroMinThreshold() public {
|
||||
vm.expectRevert(Errors.InitParamsInvalid.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
1,
|
||||
2,
|
||||
address(3),
|
||||
abi.encode(GateParams({tokenAddress: address(currency), minThreshold: 0}))
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotCallInitializeFromNonHub() public {
|
||||
vm.expectRevert(Errors.NotHub.selector);
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
profileId,
|
||||
1,
|
||||
profileOwner,
|
||||
abi.encode(GateParams({tokenAddress: address(currency), minThreshold: 1}))
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessCommentFromNonHub() public {
|
||||
vm.expectRevert(Errors.NotHub.selector);
|
||||
tokenGatedReferenceModule.processComment(
|
||||
Types.ProcessCommentParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: profileId,
|
||||
pointedPubId: 1,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessQuoteFromNonHub() public {
|
||||
vm.expectRevert(Errors.NotHub.selector);
|
||||
tokenGatedReferenceModule.processQuote(
|
||||
Types.ProcessQuoteParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: profileId,
|
||||
pointedPubId: 1,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessMirrorFromNonHub() public {
|
||||
vm.expectRevert(Errors.NotHub.selector);
|
||||
tokenGatedReferenceModule.processMirror(
|
||||
Types.ProcessMirrorParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: profileId,
|
||||
pointedPubId: 1,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Scenarios
|
||||
function testCanInitializeTokenGatedReferenceModule(uint256 profileId, uint256 pubId, uint256 minThreshold) public {
|
||||
vm.assume(profileId != 0);
|
||||
vm.assume(pubId != 0);
|
||||
vm.assume(minThreshold != 0);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
profileId,
|
||||
pubId,
|
||||
address(0),
|
||||
abi.encode(GateParams({tokenAddress: address(currency), minThreshold: minThreshold}))
|
||||
);
|
||||
}
|
||||
|
||||
function testCreatePublicationWithTokenGatedReferenceModule_EmitsExpectedEvents(
|
||||
uint256 profileId,
|
||||
uint256 pubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
vm.assume(profileId != 0);
|
||||
vm.assume(pubId != 0);
|
||||
vm.assume(minThreshold != 0);
|
||||
|
||||
vm.expectEmit(true, true, true, true, address(tokenGatedReferenceModule));
|
||||
emit TokenGatedReferencePublicationCreated(profileId, pubId, address(currency), minThreshold);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
profileId,
|
||||
pubId,
|
||||
address(0),
|
||||
abi.encode(GateParams({tokenAddress: address(currency), minThreshold: minThreshold}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/////////
|
||||
// ERC20-Gated Reference
|
||||
//
|
||||
contract TokenGatedReferenceModule_ERC20_Gated is TokenGatedReferenceModuleBase {
|
||||
function _initialize(uint256 publisherProfileId, uint256 publisherPubId, uint256 minThreshold) internal {
|
||||
vm.assume(publisherProfileId != 0);
|
||||
vm.assume(publisherPubId != 0);
|
||||
vm.assume(minThreshold != 0);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
publisherProfileId,
|
||||
publisherPubId,
|
||||
address(0),
|
||||
abi.encode(GateParams({tokenAddress: address(currency), minThreshold: minThreshold}))
|
||||
);
|
||||
}
|
||||
|
||||
constructor() TokenGatedReferenceModuleBase() {}
|
||||
|
||||
// Negatives
|
||||
function testCannotProcessComment_IfNotEnoughBalance(
|
||||
uint256 publisherProfileId,
|
||||
uint256 publisherPubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
assertEq(currency.balanceOf(address(profileOwner)), 0);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId, minThreshold);
|
||||
|
||||
vm.expectRevert(TokenGatedReferenceModule.NotEnoughBalance.selector);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processComment(
|
||||
Types.ProcessCommentParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessMirror_IfNotEnoughBalance(
|
||||
uint256 publisherProfileId,
|
||||
uint256 publisherPubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
assertEq(currency.balanceOf(address(profileOwner)), 0);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId, minThreshold);
|
||||
|
||||
vm.expectRevert(TokenGatedReferenceModule.NotEnoughBalance.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processMirror(
|
||||
Types.ProcessMirrorParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessQuote_IfNotEnoughBalance(
|
||||
uint256 publisherProfileId,
|
||||
uint256 publisherPubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
assertEq(currency.balanceOf(address(profileOwner)), 0);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId, minThreshold);
|
||||
|
||||
vm.expectRevert(TokenGatedReferenceModule.NotEnoughBalance.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processQuote(
|
||||
Types.ProcessQuoteParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Scenarios
|
||||
function testProcessComment_HoldingEnoughTokens(
|
||||
uint256 publisherProfileId,
|
||||
uint256 publisherPubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
currency.mint(profileOwner, minThreshold);
|
||||
assertTrue(currency.balanceOf(profileOwner) >= minThreshold);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId, minThreshold);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processComment(
|
||||
Types.ProcessCommentParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testProcessMirror_HoldingEnoughTokens(
|
||||
uint256 publisherProfileId,
|
||||
uint256 publisherPubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
currency.mint(profileOwner, minThreshold);
|
||||
assertTrue(currency.balanceOf(profileOwner) >= minThreshold);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId, minThreshold);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processMirror(
|
||||
Types.ProcessMirrorParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testProcessQuote_HoldingEnoughTokens(
|
||||
uint256 publisherProfileId,
|
||||
uint256 publisherPubId,
|
||||
uint256 minThreshold
|
||||
) public {
|
||||
currency.mint(profileOwner, minThreshold);
|
||||
assertTrue(currency.balanceOf(profileOwner) >= minThreshold);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId, minThreshold);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processQuote(
|
||||
Types.ProcessQuoteParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/////////
|
||||
// ERC721-Gated Reference
|
||||
//
|
||||
contract TokenGatedReferenceModule_ERC721_Gated is TokenGatedReferenceModuleBase {
|
||||
uint256 constant minThreshold = 1;
|
||||
|
||||
function _initialize(uint256 publisherProfileId, uint256 publisherPubId) internal {
|
||||
vm.assume(publisherProfileId != 0);
|
||||
vm.assume(publisherPubId != 0);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.initializeReferenceModule(
|
||||
publisherProfileId,
|
||||
publisherPubId,
|
||||
address(0),
|
||||
abi.encode(GateParams({tokenAddress: address(nft), minThreshold: minThreshold}))
|
||||
);
|
||||
}
|
||||
|
||||
constructor() TokenGatedReferenceModuleBase() {}
|
||||
|
||||
// Negatives
|
||||
function testCannotProcessComment_IfNotEnoughBalance(uint256 publisherProfileId, uint256 publisherPubId) public {
|
||||
assertEq(nft.balanceOf(address(profileOwner)), 0);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId);
|
||||
|
||||
vm.expectRevert(TokenGatedReferenceModule.NotEnoughBalance.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processComment(
|
||||
Types.ProcessCommentParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessMirror_IfNotEnoughBalance(uint256 publisherProfileId, uint256 publisherPubId) public {
|
||||
assertEq(nft.balanceOf(address(profileOwner)), 0);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId);
|
||||
|
||||
vm.expectRevert(TokenGatedReferenceModule.NotEnoughBalance.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processMirror(
|
||||
Types.ProcessMirrorParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testCannotProcessQuote_IfNotEnoughBalance(uint256 publisherProfileId, uint256 publisherPubId) public {
|
||||
assertEq(nft.balanceOf(address(profileOwner)), 0);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId);
|
||||
|
||||
vm.expectRevert(TokenGatedReferenceModule.NotEnoughBalance.selector);
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processQuote(
|
||||
Types.ProcessQuoteParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Scenarios
|
||||
function testProcessComment_HoldingEnoughTokens(uint256 publisherProfileId, uint256 publisherPubId) public {
|
||||
nft.mint({to: profileOwner, nftId: 1});
|
||||
assertTrue(nft.balanceOf(profileOwner) >= minThreshold);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processComment(
|
||||
Types.ProcessCommentParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testProcessMirror_HoldingEnoughTokens(uint256 publisherProfileId, uint256 publisherPubId) public {
|
||||
nft.mint({to: profileOwner, nftId: 1});
|
||||
assertTrue(nft.balanceOf(profileOwner) >= minThreshold);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processMirror(
|
||||
Types.ProcessMirrorParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function testProcessQuote_HoldingEnoughTokens(uint256 publisherProfileId, uint256 publisherPubId) public {
|
||||
nft.mint({to: profileOwner, nftId: 1});
|
||||
assertTrue(nft.balanceOf(profileOwner) >= minThreshold);
|
||||
|
||||
_initialize(publisherProfileId, publisherPubId);
|
||||
|
||||
vm.prank(address(hub));
|
||||
tokenGatedReferenceModule.processQuote(
|
||||
Types.ProcessQuoteParams({
|
||||
profileId: profileId,
|
||||
transactionExecutor: profileOwner,
|
||||
pointedProfileId: publisherProfileId,
|
||||
pointedPubId: publisherPubId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerPubTypes: _emptyPubTypesArray(),
|
||||
data: ''
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
11
test/mocks/NFT.sol
Normal file
11
test/mocks/NFT.sol
Normal file
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';
|
||||
|
||||
contract NFT is ERC721('NFT', 'NFT') {
|
||||
function mint(address to, uint256 nftId) external {
|
||||
_mint(to, nftId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user