diff --git a/components/custom/EventDescription.tsx b/components/custom/EventDescription.tsx index b790ae9..9aaa346 100644 --- a/components/custom/EventDescription.tsx +++ b/components/custom/EventDescription.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Card, CardHeader, @@ -32,8 +32,10 @@ const EventDescription: React.FC = ({ eventDetails, }) => { const { toast } = useToast(); + const [numTickets, setNumTickets] = useState(1); + const handleBuyNow = () => { - buyHandler(eventDetails.EventID, toast); + buyHandler(eventDetails.EventID, numTickets, toast); }; return ( @@ -79,7 +81,10 @@ const EventDescription: React.FC = ({
diff --git a/components/custom/TicketButton.tsx b/components/custom/TicketButton.tsx index 0d42a1f..7c330f1 100644 --- a/components/custom/TicketButton.tsx +++ b/components/custom/TicketButton.tsx @@ -1,6 +1,5 @@ -'use client'; -import React, { useState } from 'react'; -import { Button } from '../ui/button'; // Adjust import path to where your shadcn Button component is located +import React from 'react'; +import { Button } from '../ui/button'; interface NumberPickerProps { initialCount?: number; @@ -15,25 +14,23 @@ const NumberPicker: React.FC = ({ max = 10, onChange, }) => { - const [count, setCount] = useState(initialCount); + const [count, setCount] = React.useState(initialCount); + + React.useEffect(() => { + if (onChange) { + onChange(count); + } + }, [count, onChange]); const increment = () => { if (count < max) { - const newCount = count + 1; - setCount(newCount); - if (onChange) { - onChange(newCount); - } + setCount(count + 1); } }; const decrement = () => { if (count > min) { - const newCount = count - 1; - setCount(newCount); - if (onChange) { - onChange(newCount); - } + setCount(count - 1); } }; diff --git a/contracts/EventManagerABI.json b/contracts/EventManagerABI.json new file mode 100644 index 0000000..4d7c5b0 --- /dev/null +++ b/contracts/EventManagerABI.json @@ -0,0 +1,504 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_ticketId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "bool", + "name": "_allowed", + "type": "bool" + } + ], + "name": "approveTicket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_eventId", + "type": "uint256" + } + ], + "name": "buyTicket", + "outputs": [ + { + "internalType": "uint256", + "name": "_ticketId", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_description", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_capacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_ticketPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_eventDate", + "type": "uint256" + }, + { + "internalType": "string[]", + "name": "_images", + "type": "string[]" + } + ], + "name": "createEvent", + "outputs": [ + { + "internalType": "uint256", + "name": "_eventId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "eventId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "eventDate", + "type": "uint256" + } + ], + "name": "EventCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "ticketId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "eventId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "name": "TicketPurchased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "ticketId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "trustee", + "type": "address" + } + ], + "name": "TicketTransferApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "ticketId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "TicketTransferred", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_ticketId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferTicket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_ticketId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + } + ], + "name": "transferTicketFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_cents", + "type": "uint256" + } + ], + "name": "centsToFlare", + "outputs": [ + { + "internalType": "uint256", + "name": "_flr", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eventCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "events", + "outputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "uint256", + "name": "capacity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ticketsSold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ticketPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "eventDate", + "type": "uint256" + }, + { + "internalType": "address payable", + "name": "eventHost", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "feedIds", + "outputs": [ + { + "internalType": "bytes21", + "name": "", + "type": "bytes21" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_eventId", + "type": "uint256" + } + ], + "name": "getEventImages", + "outputs": [ + { + "internalType": "string[]", + "name": "", + "type": "string[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_eventId", + "type": "uint256" + } + ], + "name": "getEventPriceFlare", + "outputs": [ + { + "internalType": "uint256", + "name": "_flr", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_eventId", + "type": "uint256" + } + ], + "name": "getEventTickets", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFlareFeed", + "outputs": [ + { + "internalType": "uint256", + "name": "_feedValue", + "type": "uint256" + }, + { + "internalType": "int8", + "name": "_decimals", + "type": "int8" + }, + { + "internalType": "uint64", + "name": "_timestamp", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getFtsoV2CurrentFeedValues", + "outputs": [ + { + "internalType": "uint256[]", + "name": "_feedValues", + "type": "uint256[]" + }, + { + "internalType": "int8[]", + "name": "_decimals", + "type": "int8[]" + }, + { + "internalType": "uint64", + "name": "_timestamp", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ticketCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "tickets", + "outputs": [ + { + "internalType": "address", + "name": "holder", + "type": "address" + }, + { + "internalType": "uint256", + "name": "boughtTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "eventId", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "userTickets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/lib/buyHandler.ts b/lib/buyHandler.ts index bc0ed6d..02a28b2 100644 --- a/lib/buyHandler.ts +++ b/lib/buyHandler.ts @@ -17,6 +17,7 @@ type ToastFunction = (options: { export const buyHandler = async ( eventId: number, + numTickets: number, toast: ToastFunction ): Promise => { if (eventId < 0) { @@ -24,42 +25,62 @@ export const buyHandler = async ( return; } - try { - if (typeof window.ethereum === 'undefined') { - toast({ - title: 'Please install MetaMask or another Ethereum wallet', - variant: 'destructive', - }); - return; - } - - const provider = new ethers.providers.Web3Provider(window.ethereum); - const signer = provider.getSigner(); - const contract = getContract().connect(signer); - - let ticketCost = await contract.getEventPriceFlare(eventId); - ticketCost = ticketCost.mul(105).div(100); - const balance = await provider.getBalance(await signer.getAddress()); - - if (balance.lt(ticketCost)) { - toast({ - title: 'Insufficient balance to cover ticket cost and gas fees.', - variant: 'destructive', - }); - return; - } - - const tx = await contract.buyTicket(eventId, { value: ticketCost }); - const receipt = await tx.wait(); - + if (numTickets <= 0) { toast({ - title: `Ticket purchased successfully! Transaction Hash: ${receipt.transactionHash}`, - }); - } catch (error) { - console.error('Error buying ticket:', error); - toast({ - title: 'Transaction failed. Please try again.', + title: 'Please select at least one ticket.', variant: 'destructive', }); + return; + } + + toast({ + title: + 'You might be asked to approve multiple transactions if you buy multiple tickets', + }); + + while (numTickets > 0) { + try { + if (typeof window.ethereum === 'undefined') { + toast({ + title: 'Please install MetaMask or another Ethereum wallet', + variant: 'destructive', + }); + return; + } + + const provider = new ethers.providers.Web3Provider(window.ethereum); + const signer = provider.getSigner(); + const contract = getContract().connect(signer); + + const singleTicketCost = await contract.getEventPriceFlare(eventId); + const totalTicketCost = singleTicketCost + .mul(numTickets) + .mul(105) + .div(100); + + const balance = await provider.getBalance(await signer.getAddress()); + if (balance.lt(totalTicketCost)) { + toast({ + title: 'Insufficient balance to cover ticket cost and gas fees.', + variant: 'destructive', + }); + return; + } + + const tx = await contract.buyTicket(eventId, { value: totalTicketCost }); + const receipt = await tx.wait(); + + toast({ + title: `Tickets purchased successfully! Transaction Hash: ${receipt.transactionHash}`, + }); + + numTickets -= 1; + } catch (error) { + console.error('Error buying tickets:', error); + toast({ + title: 'Transaction failed. Please try again.', + variant: 'destructive', + }); + } } }; diff --git a/lib/ethers.ts b/lib/ethers.ts index f16d3ab..09df0fb 100644 --- a/lib/ethers.ts +++ b/lib/ethers.ts @@ -1,4 +1,5 @@ import { ethers } from 'ethers'; +import EventManagerABI from '../contracts/EventManagerABI.json'; const FLARE_TESTNET_RPC_URL = process.env.NEXT_PUBLIC_RPC_URL; const CONTRACT_ADDRESS = process.env.NEXT_PUBLIC_CONTRACT_ADDRESS; @@ -12,509 +13,6 @@ export function getFlareProvider() { export function getContract() { const provider = getFlareProvider(); const contractAddress = CONTRACT_ADDRESS; - const contractABI = [ - { - inputs: [ - { - internalType: 'uint256', - name: '_ticketId', - type: 'uint256', - }, - { - internalType: 'address', - name: '_to', - type: 'address', - }, - { - internalType: 'bool', - name: '_allowed', - type: 'bool', - }, - ], - name: 'approveTicket', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_eventId', - type: 'uint256', - }, - ], - name: 'buyTicket', - outputs: [ - { - internalType: 'uint256', - name: '_ticketId', - type: 'uint256', - }, - ], - stateMutability: 'payable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'string', - name: '_name', - type: 'string', - }, - { - internalType: 'string', - name: '_description', - type: 'string', - }, - { - internalType: 'uint256', - name: '_capacity', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '_ticketPrice', - type: 'uint256', - }, - { - internalType: 'uint256', - name: '_eventDate', - type: 'uint256', - }, - { - internalType: 'string[]', - name: '_images', - type: 'string[]', - }, - ], - name: 'createEvent', - outputs: [ - { - internalType: 'uint256', - name: '_eventId', - type: 'uint256', - }, - ], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [], - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint256', - name: 'eventId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'string', - name: 'name', - type: 'string', - }, - { - indexed: false, - internalType: 'uint256', - name: 'eventDate', - type: 'uint256', - }, - ], - name: 'EventCreated', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint256', - name: 'ticketId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'uint256', - name: 'eventId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'address', - name: 'buyer', - type: 'address', - }, - { - indexed: false, - internalType: 'uint256', - name: 'price', - type: 'uint256', - }, - ], - name: 'TicketPurchased', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint256', - name: 'ticketId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'address', - name: 'owner', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'trustee', - type: 'address', - }, - ], - name: 'TicketTransferApproved', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: false, - internalType: 'uint256', - name: 'ticketId', - type: 'uint256', - }, - { - indexed: false, - internalType: 'address', - name: 'from', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'to', - type: 'address', - }, - ], - name: 'TicketTransferred', - type: 'event', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_ticketId', - type: 'uint256', - }, - { - internalType: 'address', - name: '_to', - type: 'address', - }, - ], - name: 'transferTicket', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_ticketId', - type: 'uint256', - }, - { - internalType: 'address', - name: '_to', - type: 'address', - }, - ], - name: 'transferTicketFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_cents', - type: 'uint256', - }, - ], - name: 'centsToFlare', - outputs: [ - { - internalType: 'uint256', - name: '_flr', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'eventCounter', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'events', - outputs: [ - { - internalType: 'string', - name: 'name', - type: 'string', - }, - { - internalType: 'string', - name: 'description', - type: 'string', - }, - { - internalType: 'uint256', - name: 'capacity', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'ticketsSold', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'ticketPrice', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'eventDate', - type: 'uint256', - }, - { - internalType: 'address payable', - name: 'eventHost', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'feedIds', - outputs: [ - { - internalType: 'bytes21', - name: '', - type: 'bytes21', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_eventId', - type: 'uint256', - }, - ], - name: 'getEventImages', - outputs: [ - { - internalType: 'string[]', - name: '', - type: 'string[]', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_eventId', - type: 'uint256', - }, - ], - name: 'getEventPriceFlare', - outputs: [ - { - internalType: 'uint256', - name: '_flr', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '_eventId', - type: 'uint256', - }, - ], - name: 'getEventTickets', - outputs: [ - { - internalType: 'uint256[]', - name: '', - type: 'uint256[]', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getFlareFeed', - outputs: [ - { - internalType: 'uint256', - name: '_feedValue', - type: 'uint256', - }, - { - internalType: 'int8', - name: '_decimals', - type: 'int8', - }, - { - internalType: 'uint64', - name: '_timestamp', - type: 'uint64', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'getFtsoV2CurrentFeedValues', - outputs: [ - { - internalType: 'uint256[]', - name: '_feedValues', - type: 'uint256[]', - }, - { - internalType: 'int8[]', - name: '_decimals', - type: 'int8[]', - }, - { - internalType: 'uint64', - name: '_timestamp', - type: 'uint64', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [], - name: 'ticketCounter', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'tickets', - outputs: [ - { - internalType: 'address', - name: 'holder', - type: 'address', - }, - { - internalType: 'uint256', - name: 'boughtTime', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'eventId', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - { - inputs: [ - { - internalType: 'address', - name: '', - type: 'address', - }, - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - name: 'userTickets', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, - ]; + const contractABI = EventManagerABI; return new ethers.Contract(contractAddress!, contractABI, provider); } diff --git a/package-lock.json b/package-lock.json index 0916733..ff74cd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6794,6 +6794,7 @@ "integrity": "sha512-bdM5cEGCOhDSwminryHJbRmXc1x7dPKg6Pqns3qyTwFlxsqUgxE29lsERS3PlIW1HTjoIGMUqsk1zQQwST1Yxw==", "dev": true, "hasInstallScript": true, + "optional": true, "dependencies": { "node-gyp-build": "4.3.0" }, @@ -6806,6 +6807,7 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", "dev": true, + "optional": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -19793,6 +19795,7 @@ "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz", "integrity": "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==", "hasInstallScript": true, + "peer": true, "dependencies": { "elliptic": "^6.5.7", "node-addon-api": "^5.0.0", @@ -19805,12 +19808,14 @@ "node_modules/secp256k1/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "peer": true }, "node_modules/secp256k1/node_modules/elliptic": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "peer": true, "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -19824,7 +19829,8 @@ "node_modules/secp256k1/node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "peer": true }, "node_modules/seedrandom": { "version": "3.0.5",