Created
July 12, 2025 06:07
-
-
Save korrio/a6b53ecf2652f754c53b19684fbaccaf to your computer and use it in GitHub Desktop.
SelfSellingNFT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.19; | |
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; | |
contract SelfSellingNFT is ERC721, Ownable, ReentrancyGuard { | |
uint256 private _tokenIdCounter; | |
// Mapping จาก tokenId ไปยังราคาขาย | |
mapping(uint256 => uint256) public tokenPrices; | |
// Mapping จาก tokenId ไปยังสถานะการขาย | |
mapping(uint256 => bool) public tokenForSale; | |
// Events | |
event TokenMinted(uint256 indexed tokenId, address indexed owner, uint256 price); | |
event TokenPriceSet(uint256 indexed tokenId, uint256 price); | |
event TokenSold(uint256 indexed tokenId, address indexed from, address indexed to, uint256 price); | |
event TokenListedForSale(uint256 indexed tokenId, uint256 price); | |
event TokenRemovedFromSale(uint256 indexed tokenId); | |
constructor() ERC721("SelfSellingNFT", "SSNFT") { | |
_tokenIdCounter = 1; | |
} | |
/** | |
* @dev Mint NFT ใหม่พร้อมกำหนดราคาขาย | |
* @param to ที่อยู่ผู้รับ NFT | |
* @param price ราคาขายใน Wei | |
*/ | |
function mintWithPrice(address to, uint256 price) external onlyOwner { | |
require(to != address(0), "Cannot mint to zero address"); | |
require(price > 0, "Price must be greater than 0"); | |
uint256 tokenId = _tokenIdCounter; | |
_tokenIdCounter++; | |
_safeMint(to, tokenId); | |
tokenPrices[tokenId] = price; | |
tokenForSale[tokenId] = true; | |
emit TokenMinted(tokenId, to, price); | |
emit TokenListedForSale(tokenId, price); | |
} | |
/** | |
* @dev เจ้าของ NFT สามารถกำหนดราคาขายใหม่ได้ | |
* @param tokenId ID ของ NFT | |
* @param price ราคาใหม่ใน Wei | |
*/ | |
function setPrice(uint256 tokenId, uint256 price) external { | |
require(_exists(tokenId), "Token does not exist"); | |
require(ownerOf(tokenId) == msg.sender, "Only owner can set price"); | |
require(price > 0, "Price must be greater than 0"); | |
tokenPrices[tokenId] = price; | |
tokenForSale[tokenId] = true; | |
emit TokenPriceSet(tokenId, price); | |
emit TokenListedForSale(tokenId, price); | |
} | |
/** | |
* @dev เจ้าของ NFT สามารถยกเลิกการขายได้ | |
* @param tokenId ID ของ NFT | |
*/ | |
function removeFromSale(uint256 tokenId) external { | |
require(_exists(tokenId), "Token does not exist"); | |
require(ownerOf(tokenId) == msg.sender, "Only owner can remove from sale"); | |
tokenForSale[tokenId] = false; | |
emit TokenRemovedFromSale(tokenId); | |
} | |
/** | |
* @dev ฟังก์ชันหลักสำหรับซื้อ NFT - รับเงินแล้วโอนให้เจ้าของเดิมและโอน NFT ให้ผู้ซื้อ | |
* @param tokenId ID ของ NFT ที่ต้องการซื้อ | |
*/ | |
function buyNFT(uint256 tokenId) external payable nonReentrant { | |
require(_exists(tokenId), "Token does not exist"); | |
require(tokenForSale[tokenId], "Token is not for sale"); | |
require(msg.value >= tokenPrices[tokenId], "Insufficient payment"); | |
require(msg.sender != ownerOf(tokenId), "Owner cannot buy own token"); | |
address currentOwner = ownerOf(tokenId); | |
uint256 price = tokenPrices[tokenId]; | |
// ยกเลิกการขาย | |
tokenForSale[tokenId] = false; | |
// โอนเงินให้เจ้าของเดิม | |
(bool success, ) = payable(currentOwner).call{value: price}(""); | |
require(success, "Payment transfer failed"); | |
// คืนเงินส่วนเกิน (ถ้ามี) | |
if (msg.value > price) { | |
(bool refundSuccess, ) = payable(msg.sender).call{value: msg.value - price}(""); | |
require(refundSuccess, "Refund failed"); | |
} | |
// โอน NFT ให้ผู้ซื้อ | |
_transfer(currentOwner, msg.sender, tokenId); | |
emit TokenSold(tokenId, currentOwner, msg.sender, price); | |
} | |
/** | |
* @dev ดูข้อมูล NFT ที่กำลังขาย | |
* @param tokenId ID ของ NFT | |
*/ | |
function getTokenInfo(uint256 tokenId) external view returns ( | |
address owner, | |
uint256 price, | |
bool forSale, | |
string memory tokenURI | |
) { | |
require(_exists(tokenId), "Token does not exist"); | |
return ( | |
ownerOf(tokenId), | |
tokenPrices[tokenId], | |
tokenForSale[tokenId], | |
tokenURI(tokenId) | |
); | |
} | |
/** | |
* @dev ดูรายการ NFT ทั้งหมดที่กำลังขาย | |
*/ | |
function getTokensForSale() external view returns (uint256[] memory) { | |
uint256 totalTokens = _tokenIdCounter - 1; | |
uint256 forSaleCount = 0; | |
// นับจำนวน token ที่กำลังขาย | |
for (uint256 i = 1; i <= totalTokens; i++) { | |
if (_exists(i) && tokenForSale[i]) { | |
forSaleCount++; | |
} | |
} | |
// สร้าง array และเก็บ tokenId ที่กำลังขาย | |
uint256[] memory tokensForSale = new uint256[](forSaleCount); | |
uint256 currentIndex = 0; | |
for (uint256 i = 1; i <= totalTokens; i++) { | |
if (_exists(i) && tokenForSale[i]) { | |
tokensForSale[currentIndex] = i; | |
currentIndex++; | |
} | |
} | |
return tokensForSale; | |
} | |
/** | |
* @dev ตรวจสอบว่า token มีอยู่จริงหรือไม่ | |
*/ | |
function _exists(uint256 tokenId) internal view returns (bool) { | |
return tokenId > 0 && tokenId < _tokenIdCounter; | |
} | |
/** | |
* @dev Override tokenURI เพื่อใส่ metadata | |
*/ | |
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { | |
require(_exists(tokenId), "Token does not exist"); | |
// คุณสามารถ customize metadata ได้ตามต้องการ | |
return string(abi.encodePacked("https://api.example.com/metadata/", Strings.toString(tokenId))); | |
} | |
/** | |
* @dev Emergency withdraw (เฉพาะ owner) | |
*/ | |
function emergencyWithdraw() external onlyOwner { | |
uint256 balance = address(this).balance; | |
require(balance > 0, "No funds to withdraw"); | |
(bool success, ) = payable(owner()).call{value: balance}(""); | |
require(success, "Withdraw failed"); | |
} | |
/** | |
* @dev รับ Ether โดยตรง (สำหรับกรณีฉุกเฉิน) | |
*/ | |
receive() external payable { | |
// สามารถรับ Ether ได้ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment