###### 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
要怎麼丟到前台