Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions erc20/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Node Modules
node_modules/

# Hardhat generated folders
artifacts/
cache/
typechain-types/
typechain/
coverage/
coverage.json

# Environment Variables (NEVER PUSH YOUR PRIVATE KEYS!)
.env

# Custom Ignored Files for this Push
contracts/DeployedMarketplace.md
scripts/testMarketplace.ts
contracts/ERC20.sol
contracts/ERC721.sol
71 changes: 71 additions & 0 deletions erc20/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!-- faith@FAITHY:~/blockchain-projects/erc20$ npx hardhat build

Compiled 1 Solidity file with solc 0.8.28 (evm target: cancun)
No Solidity tests to compile
faith@FAITHY:~/blockchain-projects/erc20$ npx hardhat ignition deploy ignition/modules/ERC721.ts --network sepolia
[hardhat-keystore] Enter the password: **************
✔ Confirm deploy to network sepolia (11155111)? … yes



Hardhat Ignition 🚀

Resuming existing deployment from ./ignition/deployments/chain-11155111

Deploying [ ERC721Module ]

Warning - previously executed futures are not in the module:
- ERC20Module#ERC20

Batch #1
Executed ERC721Module#ERC721

[ ERC721Module ] successfully deployed 🚀

Deployed Addresses

ERC20Module#ERC20 - 0xCa6568F3f296657F1746292853FA24b86373A3A2
ERC721Module#ERC721 - 0xe4f44FCB12bfe1996163106177c44FA64560A9Ff
faith@FAITHY:~/blockchain-projects/erc20$ npx hardhat verify --network sepolia token/contract address constructors -->

<!-- Verify -->

<!-- faith@FAITHY:~/blockchain-projects/erc20/contracts$ npx hardhat keystore set SEPOLIA_RPC_URL --force
[hardhat-keystore] Enter the password: **************
[hardhat-keystore] Enter secret to store in the production keystore: ********************
Key "SEPOLIA_RPC_URL" set in the production keystore
faith@FAITHY:~/blockchain-projects/erc20/contracts$ npx hardhat verify --network sepolia --contract contracts/ERC721.sol:ERC721 0xe4f44FCB12bfe1996163106177c44FA64560A9Ff "DEFI-WOMAN" "WIDNFT" "ipfs://bafybeihwaq4ucad3mu2sgrtynahjqhzwwategsizfn4vpgthg7lxcwg4g4/"

=== Etherscan ===
[hardhat-keystore] Enter the password: **************
HHE80029: The Etherscan API key is empty.

=== Blockscout ===
HHE80001: The request to https://eth-sepolia.blockscout.com/api failed with the message "". This error comes from Blockscout, not Hardhat.

=== Sourcify ===

📤 Submitted source code for verification on Sourcify:

contracts/ERC721.sol:ERC721
Address: 0xe4f44FCB12bfe1996163106177c44FA64560A9Ff

⏳ Waiting for verification result...


✅ Contract verified successfully on Sourcify!

contracts/ERC721.sol:ERC721
Explorer: https://sourcify.dev/server/repo-ui/11155111/0xe4f44FCB12bfe1996163106177c44FA64560A9Ff -->

<!-- MINT -->

<!-- faith@FAITHY:~/blockchain-projects/erc20$ npx hardhat run scripts/mint.ts

Wallet connected: 0x32e431575062f115be156a19C13bA4aa29d44065
Sending mint transaction for Token #1...
Transaction sent! Hash: 0x76bbd34b903f21dae629d9a691b81b62618b3b4d1fee7d63ba5cb394d9755acd
Waiting for block confirmation...
✅ Minted Token 1 successfully!
faith@FAITHY:~/blockchain-projects/erc20$ -->

19 changes: 19 additions & 0 deletions erc20/contracts/Counter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

contract Counter {
uint public x;

event Increment(uint by);

function inc() public {
x++;
emit Increment(1);
}

function incBy(uint by) public {
require(by > 0, "incBy: increment should be positive");
x += by;
emit Increment(by);
}
}
32 changes: 32 additions & 0 deletions erc20/contracts/Counter.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;

import {Counter} from "./Counter.sol";
import {Test} from "forge-std/Test.sol";

// Solidity tests are compatible with foundry, so they
// use the same syntax and offer the same functionality.

contract CounterTest is Test {
Counter counter;

function setUp() public {
counter = new Counter();
}

function test_InitialValue() public view {
require(counter.x() == 0, "Initial value should be 0");
}

function testFuzz_Inc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
require(counter.x() == x, "Value after calling inc x times should be x");
}

function test_IncByZero() public {
vm.expectRevert();
counter.incBy(0);
}
}
123 changes: 123 additions & 0 deletions erc20/contracts/NFTmarketplace.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

// ERC721 Interface
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address);
function getApproved(uint256 tokenId) external view returns (address);
function isApprovedForAll(address owner, address operator) external view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) external;
}

contract NFTmarketplace {
// Reentrancy guard
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;

modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}

// Marketplace state & setting
address public owner;
address payable public treasury;
uint256 public feePercentage; // e.g., 25 for 2.5%

struct Listing {
address seller;
uint256 price;
}

// Maps
mapping(address => mapping(uint256 => Listing)) public listings; // NFT contract => tokenId => Listing

event ItemListed(address indexed seller, address indexed nftAddress, uint256 indexed tokenId, uint256 price);
event ListingCancelled(address indexed seller, address indexed nftAddress, uint256 indexed tokenId);
event ItemBought(address indexed buyer, address indexed nftAddress, uint256 indexed tokenId, uint256 price);

constructor(address payable _treasury) {
owner = msg.sender;
treasury = _treasury;
feePercentage = 25;
_status = _NOT_ENTERED;
}

modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this");
_;
}

// Marketplace fee
function setFeePercentage(uint256 _newFee) external onlyOwner {
feePercentage = _newFee;
}

// Listing NFTs
function listNFT(address nftAddress, uint256 tokenId, uint256 price) external {
require(price > 0, "Price must be > 0");

IERC721 nft = IERC721(nftAddress);
require(nft.ownerOf(tokenId) == msg.sender, "Not the owner");

require(
nft.getApproved(tokenId) == address(this) || nft.isApprovedForAll(msg.sender, address (this)),
"Marketplace not approved"
);

// Escrow the NFT
nft.transferFrom(msg.sender, address(this), tokenId);

// Save the listing
listings[nftAddress][tokenId] = Listing(msg.sender, price);

emit ItemListed(msg.sender, nftAddress, tokenId, price);
}

// Cancel Listing
function cancelListing(address nftAddress, uint256 tokenId) external nonReentrant {
Listing memory listing = listings[nftAddress][tokenId];

require(listing.price > 0, "Not Listed");
require(listing.seller == msg.sender, "Not the selleer");

// Remove Listing
delete listings[nftAddress][tokenId];

// Return NFT back to Seller
IERC721(nftAddress).transferFrom(address(this), msg.sender, tokenId);

emit ListingCancelled(msg.sender, nftAddress, tokenId);
}

// Buy NFT
function buyNFT(address nftAddress, uint256 tokenId) external payable nonReentrant {
Listing memory listing = listings[nftAddress][tokenId];

require(listing.price > 0, "Not Listed");
require(msg.value >= listing.price, "Insufficient payment");

// Calculate fee and seller cut
uint256 fee = (msg.value * feePercentage) / 1000;
uint256 sellerProceeds = msg.value - fee;

// Remove listing to prevent reentrancy
delete listings[nftAddress][tokenId];

// Transfer NFT to buyer
IERC721(nftAddress).transferFrom(address(this), msg.sender, tokenId);

// Transfer funds to treasury
(bool feeSuccess, ) = treasury.call{value: fee}("");
require(feeSuccess, "Treasury transfer failed");

// Transfer remaining funds to seller
(bool sellerSuccesss, ) = payable(listing.seller).call{value: sellerProceeds}("");
require(sellerSuccesss, "Seller transfer failed");

emit ItemBought(msg.sender, nftAddress, tokenId, listing.price);
}
}
40 changes: 40 additions & 0 deletions erc20/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import hardhatToolboxMochaEthersPlugin from "@nomicfoundation/hardhat-toolbox-mocha-ethers";
import { configVariable, defineConfig } from "hardhat/config";

export default defineConfig({
plugins: [hardhatToolboxMochaEthersPlugin],
solidity: {
profiles: {
default: {
version: "0.8.28",
},
production: {
version: "0.8.28",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
},
},
networks: {
hardhatMainnet: {
type: "edr-simulated",
chainType: "l1",
},
hardhatOp: {
type: "edr-simulated",
chainType: "op",
},
sepolia: {
type: "http",
chainType: "l1",
url: configVariable("SEPOLIA_RPC_URL"),
accounts: [configVariable("SEPOLIA_PRIVATE_KEY")],
},
},

// 7e318ba7f9c52530342d2ae81ccb02da9156d4c49121d541ac8de41d0d6ce619
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"_format": "hh3-sol-build-info-1",
"id": "solc-0_8_28-46340a8327d1ad8f41ff4718c2249f1e0ea18999",
"solcVersion": "0.8.28",
"solcLongVersion": "0.8.28+commit.7893614a",
"userSourceNameMap": {
"contracts/NFTmarketplace.sol": "project/contracts/NFTmarketplace.sol"
},
"input": {
"language": "Solidity",
"settings": {
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"metadata"
]
}
},
"remappings": []
},
"sources": {
"project/contracts/NFTmarketplace.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\n// ERC721 Interface\ninterface IERC721 {\n function ownerOf(uint256 tokenId) external view returns (address);\n function getApproved(uint256 tokenId) external view returns (address);\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n function transferFrom(address from, address to, uint256 tokenId) external;\n}\n\ncontract NFTmarketplace {\n // Reentrancy guard\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n uint256 private _status;\n\n modifier nonReentrant() {\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n _status = _ENTERED;\n _;\n _status = _NOT_ENTERED;\n }\n\n // Marketplace state & setting\n address public owner;\n address payable public treasury;\n uint256 public feePercentage; // e.g., 25 for 2.5%\n\n struct Listing {\n address seller;\n uint256 price;\n }\n\n // Maps\n mapping(address => mapping(uint256 => Listing)) public listings; // NFT contract => tokenId => Listing\n\n event ItemListed(address indexed seller, address indexed nftAddress, uint256 indexed tokenId, uint256 price);\n event ListingCancelled(address indexed seller, address indexed nftAddress, uint256 indexed tokenId);\n event ItemBought(address indexed buyer, address indexed nftAddress, uint256 indexed tokenId, uint256 price);\n\n constructor(address payable _treasury) {\n owner = msg.sender;\n treasury = _treasury;\n feePercentage = 25;\n _status = _NOT_ENTERED;\n }\n\n modifier onlyOwner() {\n require(msg.sender == owner, \"Only owner can call this\");\n _;\n }\n\n // Marketplace fee\n function setFeePercentage(uint256 _newFee) external onlyOwner {\n feePercentage = _newFee;\n }\n\n // Listing NFTs\n function listNFT(address nftAddress, uint256 tokenId, uint256 price) external {\n require(price > 0, \"Price must be > 0\");\n\n IERC721 nft = IERC721(nftAddress);\n require(nft.ownerOf(tokenId) == msg.sender, \"Not the owner\");\n\n require(\n nft.getApproved(tokenId) == address(this) || nft.isApprovedForAll(msg.sender, address (this)),\n \"Marketplace not approved\"\n );\n\n // Escrow the NFT\n nft.transferFrom(msg.sender, address(this), tokenId);\n\n // Save the listing\n listings[nftAddress][tokenId] = Listing(msg.sender, price);\n\n emit ItemListed(msg.sender, nftAddress, tokenId, price);\n }\n\n // Cancel Listing\n function cancelListing(address nftAddress, uint256 tokenId) external nonReentrant {\n Listing memory listing = listings[nftAddress][tokenId];\n\n require(listing.price > 0, \"Not Listed\");\n require(listing.seller == msg.sender, \"Not the selleer\");\n\n // Remove Listing\n delete listings[nftAddress][tokenId];\n\n // Return NFT back to Seller\n IERC721(nftAddress).transferFrom(address(this), msg.sender, tokenId);\n\n emit ListingCancelled(msg.sender, nftAddress, tokenId);\n }\n\n // Buy NFT\n function buyNFT(address nftAddress, uint256 tokenId) external payable nonReentrant {\n Listing memory listing = listings[nftAddress][tokenId];\n\n require(listing.price > 0, \"Not Listed\");\n require(msg.value >= listing.price, \"Insufficient payment\");\n\n // Calculate fee and seller cut \n uint256 fee = (msg.value * feePercentage) / 1000;\n uint256 sellerProceeds = msg.value - fee;\n\n // Remove listing to prevent reentrancy\n delete listings[nftAddress][tokenId];\n\n // Transfer NFT to buyer\n IERC721(nftAddress).transferFrom(address(this), msg.sender, tokenId);\n\n // Transfer funds to treasury\n (bool feeSuccess, ) = treasury.call{value: fee}(\"\");\n require(feeSuccess, \"Treasury transfer failed\");\n\n // Transfer remaining funds to seller\n (bool sellerSuccesss, ) = payable(listing.seller).call{value: sellerProceeds}(\"\");\n require(sellerSuccesss, \"Seller transfer failed\");\n\n emit ItemBought(msg.sender, nftAddress, tokenId, listing.price);\n }\n}"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"NFTMarketplaceModule#NFTmarketplace": "0x5974F3d3f8518ef4DF49F22A5464a54997d262DB"
}
Loading