Skip to content

Commit 72054f2

Browse files
beihailiclaude
andcommitted
feat: add ERC-721 completion certificate contract
Add CertificateNFT.sol with configurable mint fee, owner withdrawal, and CertificateMinted event for tracking module completions. Includes Foundry-based deployment instructions for Base mainnet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c5f987c commit 72054f2

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

contracts/CertificateNFT.sol

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
5+
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
6+
import "@openzeppelin/contracts/access/Ownable.sol";
7+
8+
/**
9+
* @title Web3 Education Completion Certificate
10+
* @notice ERC-721 NFT minted when a user completes a course module.
11+
* @dev Mint fee is configurable by owner. Deploy on Base for low gas.
12+
*/
13+
contract CertificateNFT is ERC721, ERC721URIStorage, Ownable {
14+
uint256 private _nextTokenId;
15+
uint256 public mintFee;
16+
17+
event CertificateMinted(address indexed to, uint256 tokenId, string moduleId);
18+
19+
constructor(uint256 _mintFee)
20+
ERC721("Web3 Education Certificate", "W3CERT")
21+
Ownable(msg.sender)
22+
{
23+
mintFee = _mintFee;
24+
}
25+
26+
function mint(string calldata tokenURI_, string calldata moduleId) external payable {
27+
require(msg.value >= mintFee, "Insufficient mint fee");
28+
uint256 tokenId = _nextTokenId++;
29+
_safeMint(msg.sender, tokenId);
30+
_setTokenURI(tokenId, tokenURI_);
31+
emit CertificateMinted(msg.sender, tokenId, moduleId);
32+
}
33+
34+
function setMintFee(uint256 _mintFee) external onlyOwner {
35+
mintFee = _mintFee;
36+
}
37+
38+
function withdraw() external onlyOwner {
39+
payable(owner()).transfer(address(this).balance);
40+
}
41+
42+
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
43+
return super.tokenURI(tokenId);
44+
}
45+
46+
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) {
47+
return super.supportsInterface(interfaceId);
48+
}
49+
}

contracts/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Smart Contracts
2+
3+
## CertificateNFT
4+
5+
ERC-721 completion certificate deployed on Base.
6+
7+
### Prerequisites
8+
- [Foundry](https://book.getfoundry.sh/getting-started/installation)
9+
- Base mainnet ETH for gas (~$1-5)
10+
11+
### Setup
12+
```bash
13+
cd contracts
14+
forge init --no-commit
15+
forge install OpenZeppelin/openzeppelin-contracts --no-commit
16+
```
17+
18+
### Deploy
19+
```bash
20+
# Set mint fee to 0.002 ETH (~$5)
21+
forge create --rpc-url https://mainnet.base.org \
22+
--private-key $PRIVATE_KEY \
23+
CertificateNFT \
24+
--constructor-args 2000000000000000
25+
26+
# Verify on Basescan
27+
forge verify-contract $CONTRACT_ADDRESS CertificateNFT \
28+
--chain base \
29+
--constructor-args $(cast abi-encode "constructor(uint256)" 2000000000000000)
30+
```
31+
32+
### After Deployment
33+
Update `src/features/nft/contractAbi.js` with the deployed contract address.

0 commit comments

Comments
 (0)