###### tags: `畢專` # 5/26 +5/28 討論 ### 新增活動+拿掉ticket id + price ``` // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract TicketContract is ERC1155, Ownable { using Counters for Counters.Counter; struct Ticket { address owner; // 擁有者地址 uint256 eventId; // 活動ID uint256 quantity; // 數量(每個NFT代表一張票) uint256 price; // 價格 uint256 startTime; // 活動開始時間 bool refunded; // 是否已退款 bool isUsed; // 是否已使用 } mapping(address => mapping(address => bool)) private _operatorApprovals; //為了使 NFT 可以進行轉移而用於追蹤已授權的操作者。 mapping(uint256 => Ticket) public tickets; // 票ID映射到票的詳細信息 Counters.Counter private ticketIdCounter; // 票ID計數器 uint256 public constant MAX_SUPPLY = 500; // 最大供應量 uint256 public constant MAX_PER_MINT = 1; // 每次購買的最大數量 uint256 public constant REFUND_PERCENT = 80; // 退款百分比 event TicketMinted(uint256 indexed ticketId, address indexed owner); // 票被鑄造時觸發的事件 event TicketBurned(uint256 indexed ticketId); // 票被銷毀時觸發的事件 event TicketTransferred(uint256 indexed ticketId, address indexed from, address indexed to); // 票被轉移時觸發的事件 constructor() ERC1155("") {} // 鑄造票 function mintTicket(uint256 eventId, uint256 quantity, uint256 price) external payable { require(quantity <= MAX_PER_MINT, "Exceeds maximum per mint"); require(ticketIdCounter.current() + quantity <= MAX_SUPPLY, "Exceeds maximum supply"); require(msg.value == price * quantity, "Insufficient payment"); for (uint256 i = 0; i < quantity; i++) { uint256 ticketId = ticketIdCounter.current(); tickets[ticketId] = Ticket({ owner: msg.sender, eventId: eventId, quantity: 1, price: price, startTime: block.timestamp, refunded: false, isUsed: false }); _mint(msg.sender, ticketId, 1, ""); ticketIdCounter.increment(); emit TicketMinted(ticketId, msg.sender); } } // 銷毀票 function burnTicket(uint256 ticketId) external { require(_msgSender() == tickets[ticketId].owner, "Only ticket owner can burn the ticket"); require(!tickets[ticketId].isUsed, "Cannot burn a used ticket"); delete tickets[ticketId]; _burn(_msgSender(), ticketId, 1); emit TicketBurned(ticketId); } // 轉移票 function transferTicket(address to, uint256 ticketId) external { require(_msgSender() == tickets[ticketId].owner, "Only ticket owner can transfer the ticket"); require(!tickets[ticketId].isUsed, "Cannot transfer a used ticket"); tickets[ticketId].owner = to; safeTransferFrom(_msgSender(), to, ticketId, 1, ""); emit TicketTransferred(ticketId, _msgSender(), to); } // 活動結束後銷毀所有票 function burnTicketsAfterEvent(uint256 eventId) external onlyOwner { require(block.timestamp > tickets[eventId].startTime, "Event has not ended yet"); for (uint256 i = 0; i < ticketIdCounter.current(); i++) { if (tickets[i].eventId == eventId) { _burn(tickets[i].owner, i, tickets[i].quantity); emit TicketBurned(i); } } } // 退款 function refund(uint256 ticketId) external { require(tickets[ticketId].owner == msg.sender, "Not the ticket owner"); require(block.timestamp < tickets[ticketId].startTime, "Refund period has ended"); if (!tickets[ticketId].refunded) { uint256 refundAmount = (tickets[ticketId].price * REFUND_PERCENT) / 100; uint256 feeAmount = tickets[ticketId].price - refundAmount; tickets[ticketId].refunded = true; payable(tickets[ticketId].owner).transfer(refundAmount); payable(owner()).transfer(feeAmount); _burn(msg.sender, ticketId, 1); emit TicketBurned(ticketId); } } // 設置票務系統中的NFT的URI function setURI(string memory newUri) public onlyOwner { _setURI(newUri); } // 查詢總票數,展示網頁中的現在票務銷售情況 function getTotalTickets() external view returns (uint256) { return ticketIdCounter.current(); } // 檢查合約是否支持ERC1155介面 function supportsInterface(bytes4 interfaceId) public view override(ERC1155) returns (bool) { return super.supportsInterface(interfaceId); } // 提取合約中的資金 function withdraw() external onlyOwner { uint256 balance = address(this).balance; payable(owner()).transfer(balance); } // 獲取活動詳細信息 function getEvent(uint256 eventId) external view returns ( uint256 id, uint256 totalTickets, uint256 ticketPrice, uint256 startTime ) { Ticket storage ticket = tickets[eventId]; require(ticket.owner != address(0), "Event does not exist"); return ( ticket.eventId, ticketIdCounter.current(), ticket.price, ticket.startTime ); } } ``` 5/28 ### event struct(mintTicket&refund function以使用新的events映射,並在burnTicketsAfterEvent函數中修改了判斷活動是否結束) + 改成tokenId ``` // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract TicketContract is ERC1155, Ownable { using Counters for Counters.Counter; struct Event { string name; // 活動名稱 uint256 price; // 活動票價 uint256 capacity; // 人數限制 string eventType; // 活動類型 bool oneTicketPerPerson; // 是否一人限購一張 uint256 startTime; // 活動開始時間 uint256 endTime; // 活動結束時間 } mapping(uint256 => Event) public events; struct Ticket { address owner; // 擁有者地址 uint256 eventId; // 活動ID uint256 quantity; // 數量(每個NFT代表一張票) uint256 price; // 價格 uint256 startTime; // 活動開始時間 bool refunded; // 是否已退款 bool isUsed; // 是否已使用 } mapping(uint256 => Ticket) public tickets; // 票ID映射到票的詳細信息 mapping(address => mapping(uint256 => bool)) public addressHasPurchased; // 地址是否已購買特定活動的票 Counters.Counter private tokenIdCounter; // tokenId計數器 uint256 public constant MAX_SUPPLY = 500; // 最大供應量 uint256 public constant MAX_PER_MINT = 1; // 每次購買的最大數量 uint256 public constant REFUND_PERCENT = 80; // 退款百分比 event TicketMinted(uint256 indexed tokenId, address indexed owner); // 票被鑄造時觸發的事件 event TicketBurned(uint256 indexed tokenId); // 票被銷毀時觸發的事件 event TicketTransferred(uint256 indexed tokenId, address indexed from, address indexed to); // 票被轉移時觸發的事件 constructor() ERC1155("") {} // 新增活動種類 function addEvent( uint256 eventId, string memory name, uint256 price, uint256 capacity, string memory eventType, bool oneTicketPerPerson, uint256 startTime, uint256 endTime ) external onlyOwner { events[eventId] = Event({ name: name, price: price, capacity: capacity, eventType: eventType, oneTicketPerPerson: oneTicketPerPerson, startTime: startTime, endTime: endTime }); } // 鑄造票 function mintTicket(uint256 eventId, uint256 quantity) external payable { require(quantity <= MAX_PER_MINT, "Exceeds maximum per mint"); require(tokenIdCounter.current() + quantity <= MAX_SUPPLY, "Exceeds maximum supply"); require(msg.value == events[eventId].price * quantity, "Insufficient payment"); require(block.timestamp < events[eventId].endTime, "Event has already ended"); uint256 tokenId = tokenIdCounter.current(); require(events[eventId].oneTicketPerPerson && !addressHasPurchased[msg.sender][eventId], "One ticket per person rule violated"); require(quantity <= events[eventId].capacity, "Exceeds event capacity"); tickets[tokenId] = Ticket({ owner: msg.sender, eventId: eventId, quantity: quantity, price: events[eventId].price, startTime: events[eventId].startTime, refunded: false, isUsed: false }); _mint(msg.sender, tokenId, quantity, ""); tokenIdCounter.increment(); // Mark the address as having purchased tickets for the specific event addressHasPurchased[msg.sender][eventId] = true; emit TicketMinted(tokenId, msg.sender); } // 轉移票 function transferTicket(address to, uint256 tokenId) external { require(_msgSender() == tickets[tokenId].owner, "Only ticket owner can transfer the ticket"); require(!tickets[tokenId].isUsed, "Cannot transfer a used ticket"); require(block.timestamp < tickets[tokenId].startTime, "Ticket transfer not allowed after event start time"); tickets[tokenId].owner = to; safeTransferFrom(_msgSender(), to, tokenId, tickets[tokenId].quantity, ""); emit TicketTransferred(tokenId, _msgSender(), to); } // 活動結束後銷毀所有票 function burnTicketsAfterEvent(uint256 eventId) external onlyOwner { require(block.timestamp > events[eventId].endTime, "Event has not ended yet"); for (uint256 i = 0; i < tokenIdCounter.current(); i++) { if (tickets[i].eventId == eventId) { _burn(tickets[i].owner, i, tickets[i].quantity); emit TicketBurned(i); } } } // 退款 function refund(uint256 tokenId) external { require(tickets[tokenId].owner == msg.sender, "Not the ticket owner"); require(block.timestamp < events[tickets[tokenId].eventId].startTime, "Refund period has ended"); if (!tickets[tokenId].refunded) { uint256 refundAmount = (tickets[tokenId].price * REFUND_PERCENT) / 100; uint256 feeAmount = tickets[tokenId].price - refundAmount; tickets[tokenId].refunded = true; payable(tickets[tokenId].owner).transfer(refundAmount); payable(owner()).transfer(feeAmount); _burn(msg.sender, tokenId, tickets[tokenId].quantity); emit TicketBurned(tokenId); } } // 設置票務系統中的NFT的URI function setURI(string memory newUri) public onlyOwner { _setURI(newUri); } // 查詢總票數,展示網頁中的現在票務銷售情況 function getTotalTickets() external view returns (uint256) { return tokenIdCounter.current(); } // 檢查合約是否支持ERC1155介面 function supportsInterface(bytes4 interfaceId) public view override(ERC1155) returns (bool) { return super.supportsInterface(interfaceId); } // 提取合約中的資金 function withdraw() external onlyOwner { uint256 balance = address(this).balance; payable(owner()).transfer(balance); } } ``` ### 點到使用者 address 對到token 要怎麼丟到前台