Compare commits
3 Commits
247dfad12d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b883596a4e | |||
| 1e79751809 | |||
| cb790629ba |
@@ -3,8 +3,8 @@ src = "src"
|
||||
out = "out"
|
||||
libs = ["lib", "dependencies"]
|
||||
remappings = [
|
||||
"@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.2.0/",
|
||||
"solmate/=dependencies/solmate-6.8.0/",
|
||||
"openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.2.0/",
|
||||
"solmate/=dependencies/solmate-6.8.0/src/",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import {Counter} from "../src/Counter.sol";
|
||||
|
||||
contract CounterScript is Script {
|
||||
Counter public counter;
|
||||
|
||||
function setUp() public {}
|
||||
|
||||
function run() public {
|
||||
vm.startBroadcast();
|
||||
|
||||
counter = new Counter();
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
contract Counter {
|
||||
uint256 public number;
|
||||
|
||||
function setNumber(uint256 newNumber) public {
|
||||
number = newNumber;
|
||||
}
|
||||
|
||||
function increment() public {
|
||||
number++;
|
||||
}
|
||||
}
|
||||
62
src/NFT.sol
Normal file
62
src/NFT.sol
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import {ERC721} from "solmate/tokens/ERC721.sol";
|
||||
import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";
|
||||
import {Strings} from "openzeppelin-contracts/utils/Strings.sol";
|
||||
|
||||
|
||||
|
||||
contract NFT is ERC721, Ownable {
|
||||
using Strings for uint256;
|
||||
|
||||
string public baseURI;
|
||||
uint256 public currentTokenId;
|
||||
uint256 public constant TOTAL_SUPPLY = 10_000;
|
||||
uint256 public constant MINT_PRICE = 0.08 ether;
|
||||
|
||||
error MintPriceNotPaid();
|
||||
error MaxSupply();
|
||||
error NonExistentTokenURI();
|
||||
error WithdrawTransfer();
|
||||
|
||||
constructor(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
string memory _baseURI
|
||||
) ERC721(_name, _symbol) Ownable(msg.sender) {
|
||||
baseURI = _baseURI;
|
||||
}
|
||||
|
||||
function mintTo(address recipient) public payable returns (uint256) {
|
||||
if (msg.value != MINT_PRICE) {
|
||||
revert MintPriceNotPaid();
|
||||
}
|
||||
uint256 newTokenId = currentTokenId + 1;
|
||||
if (newTokenId > TOTAL_SUPPLY) {
|
||||
revert MaxSupply();
|
||||
}
|
||||
currentTokenId = newTokenId;
|
||||
_safeMint(recipient, newTokenId);
|
||||
return newTokenId;
|
||||
}
|
||||
|
||||
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
|
||||
if (ownerOf(tokenId) == address(0)) {
|
||||
revert NonExistentTokenURI();
|
||||
}
|
||||
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
|
||||
}
|
||||
|
||||
function withdrawPayments(address payable payee) external onlyOwner {
|
||||
if (address(this).balance == 0) {
|
||||
revert WithdrawTransfer();
|
||||
}
|
||||
|
||||
payable(payee).transfer(address(this).balance);
|
||||
}
|
||||
|
||||
function _checkOwner() internal view override {
|
||||
require(msg.sender == owner(), "Onable: caller is not the owner");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import {Test, console} from "forge-std/Test.sol";
|
||||
import {Counter} from "../src/Counter.sol";
|
||||
|
||||
contract CounterTest is Test {
|
||||
Counter public counter;
|
||||
|
||||
function setUp() public {
|
||||
counter = new Counter();
|
||||
counter.setNumber(0);
|
||||
}
|
||||
|
||||
function test_Increment() public {
|
||||
counter.increment();
|
||||
assertEq(counter.number(), 1);
|
||||
}
|
||||
|
||||
function testFuzz_SetNumber(uint256 x) public {
|
||||
counter.setNumber(x);
|
||||
assertEq(counter.number(), x);
|
||||
}
|
||||
}
|
||||
67
test/NFT.t.sol
Normal file
67
test/NFT.t.sol
Normal file
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import {stdStorage, StdStorage, Test} from "forge-std/Test.sol";
|
||||
import {NFT} from "src/NFT.sol";
|
||||
|
||||
contract NFTTest is Test {
|
||||
using stdStorage for StdStorage;
|
||||
|
||||
NFT private nft;
|
||||
|
||||
function setUp() public {
|
||||
nft = new NFT("NFT_Tutorial", "TUT", "baseUri");
|
||||
}
|
||||
|
||||
function test_RevertMintWithoutValue() public {
|
||||
vm.expectRevert(NFT.MintPriceNotPaid.selector);
|
||||
nft.mintTo(address(1));
|
||||
}
|
||||
|
||||
function test_MintPricePaid() public {
|
||||
nft.mintTo{value: 0.08 ether}(address(1));
|
||||
}
|
||||
|
||||
function test_RevertMintMaxSupplyReached() public {
|
||||
string memory signature = "currentTokenId()";
|
||||
uint256 slot = stdstore.target(address(nft)).sig(signature).find();
|
||||
bytes32 loc = bytes32(slot);
|
||||
bytes32 mockedCurrentTokenId = bytes32(abi.encode(10000));
|
||||
vm.store(address(nft), loc, mockedCurrentTokenId);
|
||||
vm.expectRevert(NFT.MaxSupply.selector);
|
||||
nft.mintTo{value: 0.08 ether}(address(1));
|
||||
}
|
||||
|
||||
function test_RevertMintToZeroAddress() public {
|
||||
vm.expectRevert("INVALID_RECIPIENT");
|
||||
nft.mintTo{value: 0.08 ether}(address(0));
|
||||
}
|
||||
|
||||
function test_NewMintOwnerRegistered() public {
|
||||
nft.mintTo{value: 0.08 ether}(address(1));
|
||||
uint256 slotOfNewOwner = stdstore.target(address(nft)).sig(nft.ownerOf.selector).with_key(address(1)).find();
|
||||
|
||||
uint160 ownerOfTokenIdOne = uint160(
|
||||
uint256(
|
||||
(vm.load(address(nft), bytes32(abi.encode(slotOfNewOwner))))
|
||||
)
|
||||
);
|
||||
assertEq(address(ownerOfTokenIdOne), address(1));
|
||||
}
|
||||
|
||||
function test_BalanceIncremented() public {
|
||||
nft.mintTo{value: 0.08 ether}(address(1));
|
||||
uint256 slotBalance = stdstore.target(address(nft)).sig(nft.balanceOf.selector).with_key(address(1)).find();
|
||||
|
||||
uint256 balanceFirstMint = uint256(
|
||||
vm.load(address(nft), bytes32(slotBalance))
|
||||
);
|
||||
assertEq(balanceFirstMint, 1);
|
||||
|
||||
nft.mintTo{value: 0.08 ether}(address(1));
|
||||
uint256 balanceSecondMint = uint256(
|
||||
vm.load(address(nft), bytes32(slotBalance))
|
||||
);
|
||||
assertEq(balanceSecondMint, 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user