From 372298b409e6c036cb88e787a0747881f447aca2 Mon Sep 17 00:00:00 2001 From: ayomaska18 Date: Sat, 26 Oct 2024 10:33:51 +0100 Subject: [PATCH] added new components for sc --- app/page.tsx | 5 +- components/sc/buyTicket.tsx | 119 ++++++++++++++++++ components/sc/createEvent.tsx | 34 ++++- ...tEventPrice.tsx => getEventPriceFlare.tsx} | 2 +- components/sc/getEventTickets.tsx | 25 +++- contracts/EventManager.sol | 27 ++-- lib/ethers.ts | 44 +++---- 7 files changed, 209 insertions(+), 47 deletions(-) rename components/sc/{getEventPrice.tsx => getEventPriceFlare.tsx} (94%) diff --git a/app/page.tsx b/app/page.tsx index 282bbcf..bebd006 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,10 +1,11 @@ import Image from 'next/image'; import EventCounter from '@/components/sc/eventCounter'; import CreateEvent from '@/components/sc/createEvent'; -import GetEventPrice from '@/components/sc/getEventPrice'; +import GetEventPrice from '@/components/sc/getEventPriceFlare'; import FlareFeed from '@/components/sc/getFlareFeed'; import GetEventImages from '@/components/sc/getEventImages'; import GetEventTickets from '@/components/sc/getEventTickets'; +import BuyTicket from '@/components/sc/buyTicket'; export default function Home() { return ( @@ -41,6 +42,8 @@ export default function Home() { + +
{ + const [eventId, setEventId] = useState(null); + const [transactionHash, setTransactionHash] = useState(null); + const [isWalletConnected, setIsWalletConnected] = useState(false); + const [walletAddress, setWalletAddress] = useState(null); + + // Connect Wallet + const handleConnectWallet = async () => { + try { + if (typeof window.ethereum !== 'undefined' && window.ethereum.request) { + const accounts = await window.ethereum.request({ + method: 'eth_requestAccounts', + }); + if (accounts.length > 0) { + setIsWalletConnected(true); + setWalletAddress(accounts[0]); + console.log('Wallet connected:', accounts[0]); + } + } else { + alert('Please install MetaMask or another Ethereum wallet'); + } + } catch (error) { + console.error('Error connecting to wallet:', error); + } + }; + + // Handle buying a ticket for the event + const handleBuyTicket = async () => { + if (!eventId) { + alert('Please enter a valid Event ID.'); + return; + } + + try { + // Get the provider and signer + const provider = new ethers.providers.Web3Provider(window.ethereum); + const signer = provider.getSigner(); + const contract = getContract().connect(signer); + + // Call `getEventPriceFlare` to get the ticket cost in FLR + const ticketCost = await contract.getEventPriceFlare(eventId); + console.log('Ticket cost in FLR:', ethers.utils.formatEther(ticketCost)); + + // Check wallet balance + const balance = await provider.getBalance(await signer.getAddress()); + console.log('Wallet balance in Wei:', balance.toString()); + console.log('Wallet balance in FLR:', ethers.utils.formatEther(balance)); // Converts to FLR for readability + + if (balance.lt(ticketCost)) { + alert('Insufficient balance to cover ticket cost and gas fees.'); + return; + } + + // Proceed with buying the ticket + const tx = await contract.buyTicket(eventId, { value: ticketCost }); + const receipt = await tx.wait(); + + setTransactionHash(receipt.transactionHash); + console.log( + 'Ticket bought successfully, transaction hash:', + receipt.transactionHash + ); + } catch (error) { + console.error('Error buying ticket:', error); + } + }; + + return ( +
+

Buy Ticket

+ {!isWalletConnected ? ( + + ) : ( +
+

Connected Wallet: {walletAddress}

+ setEventId(Number(e.target.value))} + className="border p-2 mb-2" + /> + + + + {transactionHash && ( +

+ Transaction successful! Hash: {transactionHash} +

+ )} +
+ )} +
+ ); +}; + +export default BuyTicket; diff --git a/components/sc/createEvent.tsx b/components/sc/createEvent.tsx index c06b6a7..ad8634d 100644 --- a/components/sc/createEvent.tsx +++ b/components/sc/createEvent.tsx @@ -14,11 +14,13 @@ const CreateEvent = () => { const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [capacity, setCapacity] = useState(0); - const [ticketPrice, setTicketPrice] = useState(0); // Price in FLR + const [ticketPrice, setTicketPrice] = useState(0); const [eventDate, setEventDate] = useState(''); const [images, setImages] = useState([]); const [transactionHash, setTransactionHash] = useState(''); const [isWalletConnected, setIsWalletConnected] = useState(false); + const [walletAddress, setWalletAddress] = useState(null); // Store the connected wallet address + const [eventId, setEventId] = useState(null); // Store the created event ID // Check if the wallet is connected on component mount useEffect(() => { @@ -29,6 +31,7 @@ const CreateEvent = () => { }); if (accounts && accounts.length > 0) { setIsWalletConnected(true); // Wallet is connected + setWalletAddress(accounts[0]); // Store the connected wallet address } else { setIsWalletConnected(false); // Wallet is not connected } @@ -48,6 +51,7 @@ const CreateEvent = () => { }); if (accounts.length > 0) { setIsWalletConnected(true); + setWalletAddress(accounts[0]); // Store the connected wallet address console.log('Wallet connected:', accounts[0]); } } @@ -75,19 +79,37 @@ const CreateEvent = () => { const contract = getContract().connect(signer); const unixEventDate = Math.floor(new Date(eventDate).getTime() / 1000); // Convert to Unix timestamp - const weiTicketPrice = ethers.utils.parseEther(ticketPrice.toString()); // Convert FLR to Wei + // Convert ticket price from dollars to cents + const centsTicketPrice = Math.round(ticketPrice * 100); // Assuming ticketPrice is entered in USD cents + + // Call the `createEvent` function, which submits the transaction const tx = await contract.createEvent( name, description, capacity, - weiTicketPrice, + centsTicketPrice, // Now this is in cents, e.g storing as 500 cents == 5.00 usd unixEventDate, images ); + + console.log('Transaction Submitted:', tx); + + // Wait for the transaction to be mined const receipt = await tx.wait(); setTransactionHash(receipt.transactionHash); - console.log('Event created successfully!'); + + // Extract the `eventId` from the event logs in the receipt + const eventId = receipt.events?.find( + (event: ethers.Event) => event.event === 'EventCreated' + )?.args?.[0]; + + if (eventId) { + setEventId(eventId.toNumber()); // Store the event ID if found + console.log('Event created successfully with ID:', eventId.toNumber()); + } else { + console.log('Event ID not found in the logs.'); + } } catch (error) { console.error('Error creating event:', error); } @@ -100,6 +122,8 @@ const CreateEvent = () => { ) : (

Create Event

+

Connected Wallet: {walletAddress}

{' '} + {/* Display connected wallet address */} { onChange={(e) => setImages(e.target.value.split(','))} /> - {transactionHash && (

Transaction successful! Hash: {transactionHash}

)} + {eventId !== null &&

Event ID: {eventId}

}
)}
diff --git a/components/sc/getEventPrice.tsx b/components/sc/getEventPriceFlare.tsx similarity index 94% rename from components/sc/getEventPrice.tsx rename to components/sc/getEventPriceFlare.tsx index b071930..8d6003a 100644 --- a/components/sc/getEventPrice.tsx +++ b/components/sc/getEventPriceFlare.tsx @@ -13,7 +13,7 @@ const GetEventPrice = () => { if (eventId === null) return; const contract = getContract(); - const flrPrice = await contract.getEventPriceFlare(eventId); + const flrPrice = await contract.getEventPriceFlare(eventId); // flr price in usd cents e.g. return 36856 meaning 500 usd cents setPriceInFlr(ethers.utils.formatEther(flrPrice.toString())); } catch (error) { console.error('Error fetching event price:', error); diff --git a/components/sc/getEventTickets.tsx b/components/sc/getEventTickets.tsx index a33876b..dcf4067 100644 --- a/components/sc/getEventTickets.tsx +++ b/components/sc/getEventTickets.tsx @@ -1,22 +1,39 @@ 'use client'; import React, { useState } from 'react'; +import { ethers } from 'ethers'; import { getContract } from '@/lib/ethers'; // Adjust the path to your ethers helper const GetEventTickets = () => { const [eventId, setEventId] = useState(null); const [tickets, setTickets] = useState(null); + const [errorMessage, setErrorMessage] = useState(null); const handleGetTickets = async () => { + setErrorMessage(null); + setTickets(null); + + if (eventId === null) { + setErrorMessage('Please enter a valid Event ID.'); + return; + } + try { + // Get the contract instance const contract = getContract(); - if (eventId === null) return; - + // Fetch tickets for the given event ID const eventTickets = await contract.getEventTickets(eventId); - setTickets(eventTickets); + + // Convert BigNumbers to plain numbers for display + setTickets( + eventTickets.map((ticket: ethers.BigNumber) => ticket.toNumber()) + ); } catch (error) { console.error('Error fetching event tickets:', error); + setErrorMessage( + 'Failed to fetch tickets. Please check the Event ID and try again.' + ); } }; @@ -37,6 +54,8 @@ const GetEventTickets = () => { Get Tickets + {errorMessage &&

{errorMessage}

} + {tickets && (

Tickets for Event {eventId}:

diff --git a/contracts/EventManager.sol b/contracts/EventManager.sol index 11ae5bb..f68a7a7 100644 --- a/contracts/EventManager.sol +++ b/contracts/EventManager.sol @@ -29,7 +29,7 @@ contract EventManager { uint256 eventDate; string[] images; // array of image URLs uint256[] tickets; - address eventHost; + address payable eventHost; } struct Ticket { @@ -71,7 +71,7 @@ contract EventManager { return _cents * power(10, decimals) * 1 ether / 100 / feedValue; } - function power(uint base, int8 exponent) public pure returns (uint) { + function power(uint base, int8 exponent) private pure returns (uint) { require(exponent >= 0, "Exponent must be non-negative"); uint result = 1; for (int8 i = 0; i < exponent; i++) { @@ -85,9 +85,10 @@ contract EventManager { return centsToFlare(events[_eventId].ticketPrice); } - function createEvent(string memory _name, string memory _description, uint256 _capacity, uint256 _ticketPrice, uint256 _eventDate, string[] memory _images) public { - events[eventCounter] = Event(_name, _description, _capacity, 0, _ticketPrice, _eventDate, _images, new uint256[](0), msg.sender); + function createEvent(string memory _name, string memory _description, uint256 _capacity, uint256 _ticketPrice, uint256 _eventDate, string[] memory _images) public returns (uint256 _eventId) { + events[eventCounter] = Event(_name, _description, _capacity, 0, _ticketPrice, _eventDate, _images, new uint256[](0), payable(msg.sender)); eventCounter++; + return eventCounter - 1; } function getEventImages(uint256 _eventId) public view returns (string[] memory) { @@ -100,12 +101,18 @@ contract EventManager { return events[_eventId].tickets; } - //TODO: ADD CURRENCY CONVERSION + CHECK - function buyTicket(uint256 _eventId) public payable { + function buyTicket(uint256 _eventId) public payable returns (uint256 _ticketId) { require(_eventId < eventCounter, "Invalid event ID"); require(events[_eventId].eventDate > block.timestamp, "Event has already passed"); require(events[_eventId].tickets.length < events[_eventId].capacity, "Event is full"); - require(msg.value == events[_eventId].ticketPrice, "Invalid ticket price"); + + uint256 ticketCost = getEventPriceFlare(_eventId); // Get ticket price in FLR + require(msg.value >= ticketCost, "Insufficient value provided"); // Ensure user has paid >= ticket price + if (msg.value > ticketCost) { + // Pay any excess the user paid + (bool sentExcess, ) = msg.sender.call{value: msg.value - ticketCost}(""); + require(sentExcess, "Failed to send FLR excess back to buyer"); + } // Create new ticket tickets[ticketCounter] = Ticket(msg.sender, block.timestamp, _eventId); @@ -119,8 +126,10 @@ contract EventManager { events[_eventId].ticketsSold++; // Transfer FLR to event host - (bool sent, ) = events[_eventId].eventHost.call{value: msg.value}(""); - require(sent, "Failed to send FLR to event host"); + (bool sentToHost, ) = events[_eventId].eventHost.call{value: ticketCost}(""); + require(sentToHost, "Failed to send FLR to event host"); + + return ticketCounter - 1; } function transferTicketForce(uint256 _ticketId, address _to) private { diff --git a/lib/ethers.ts b/lib/ethers.ts index bc08861..304b2e5 100644 --- a/lib/ethers.ts +++ b/lib/ethers.ts @@ -2,7 +2,7 @@ import { ethers } from 'ethers'; const FLARE_TESTNET_RPC_URL = 'https://coston2.enosys.global/ext/C/rpc'; -const CONTRACT_ADDRESS = '0x0B236423274D36C32fb2362cc177756a21A025A3'; +const CONTRACT_ADDRESS = '0xa67f64937a0a4daf3b5f5Eea7903d1E81d375b7b'; export function getFlareProvider() { const flareRpcUrl = FLARE_TESTNET_RPC_URL; @@ -46,7 +46,13 @@ export function getContract() { }, ], name: 'buyTicket', - outputs: [], + outputs: [ + { + internalType: 'uint256', + name: '_ticketId', + type: 'uint256', + }, + ], stateMutability: 'payable', type: 'function', }, @@ -84,7 +90,13 @@ export function getContract() { }, ], name: 'createEvent', - outputs: [], + outputs: [ + { + internalType: 'uint256', + name: '_eventId', + type: 'uint256', + }, + ], stateMutability: 'nonpayable', type: 'function', }, @@ -184,7 +196,7 @@ export function getContract() { type: 'uint256', }, { - internalType: 'address', + internalType: 'address payable', name: 'eventHost', type: 'address', }, @@ -314,30 +326,6 @@ export function getContract() { stateMutability: 'view', type: 'function', }, - { - inputs: [ - { - internalType: 'uint256', - name: 'base', - type: 'uint256', - }, - { - internalType: 'int8', - name: 'exponent', - type: 'int8', - }, - ], - name: 'power', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'pure', - type: 'function', - }, { inputs: [], name: 'ticketCounter',