From f00797a8f496287d68907aaf97a46855c1fa5d23 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:55:05 +1000 Subject: [PATCH] TD-1653: chore: add example app for Orderbook create listing feature (#2150) Co-authored-by: Craig M. --- examples/orderbook/README.md | 1 - .../create-listing-with-nextjs/.env.example | 2 + .../create-listing-with-nextjs/.eslintrc.json | 6 + .../create-listing-with-nextjs/.gitignore | 36 ++ .../create-listing-with-nextjs/README.md | 68 ++++ .../next.config.mjs | 4 + .../create-listing-with-nextjs/package.json | 29 ++ .../playwright.config.ts | 30 ++ .../app/create-listing-with-erc1155/page.tsx | 345 ++++++++++++++++++ .../app/create-listing-with-erc721/page.tsx | 333 +++++++++++++++++ .../src/app/favicon.ico | Bin 0 -> 25931 bytes .../src/app/globals.css | 18 + .../src/app/layout.tsx | 25 ++ .../src/app/logout/page.tsx | 16 + .../src/app/page.tsx | 29 ++ .../src/app/redirect/page.tsx | 19 + .../src/app/utils/listing.ts | 75 ++++ .../src/app/utils/setupOrderbook.ts | 8 + .../src/app/utils/setupPassport.ts | 18 + .../src/app/utils/wrapper.tsx | 16 + .../tests/base.spec.ts | 28 ++ .../create-listing-with-nextjs/tsconfig.json | 26 ++ package.json | 3 +- yarn.lock | 294 +++++++++++++++ 24 files changed, 1427 insertions(+), 2 deletions(-) delete mode 100644 examples/orderbook/README.md create mode 100644 examples/orderbook/create-listing-with-nextjs/.env.example create mode 100644 examples/orderbook/create-listing-with-nextjs/.eslintrc.json create mode 100644 examples/orderbook/create-listing-with-nextjs/.gitignore create mode 100644 examples/orderbook/create-listing-with-nextjs/README.md create mode 100644 examples/orderbook/create-listing-with-nextjs/next.config.mjs create mode 100644 examples/orderbook/create-listing-with-nextjs/package.json create mode 100644 examples/orderbook/create-listing-with-nextjs/playwright.config.ts create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc1155/page.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc721/page.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/favicon.ico create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/globals.css create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/layout.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/logout/page.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/page.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/redirect/page.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/utils/listing.ts create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/utils/setupOrderbook.ts create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/utils/setupPassport.ts create mode 100644 examples/orderbook/create-listing-with-nextjs/src/app/utils/wrapper.tsx create mode 100644 examples/orderbook/create-listing-with-nextjs/tests/base.spec.ts create mode 100644 examples/orderbook/create-listing-with-nextjs/tsconfig.json diff --git a/examples/orderbook/README.md b/examples/orderbook/README.md deleted file mode 100644 index b5f8f3ea0b..0000000000 --- a/examples/orderbook/README.md +++ /dev/null @@ -1 +0,0 @@ -# Coming Soon™ \ No newline at end of file diff --git a/examples/orderbook/create-listing-with-nextjs/.env.example b/examples/orderbook/create-listing-with-nextjs/.env.example new file mode 100644 index 0000000000..1947cf61ec --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_PUBLISHABLE_KEY= +NEXT_PUBLIC_CLIENT_ID= \ No newline at end of file diff --git a/examples/orderbook/create-listing-with-nextjs/.eslintrc.json b/examples/orderbook/create-listing-with-nextjs/.eslintrc.json new file mode 100644 index 0000000000..ab71342ed1 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "root": true, + "extends": [ + "next/core-web-vitals" + ] +} diff --git a/examples/orderbook/create-listing-with-nextjs/.gitignore b/examples/orderbook/create-listing-with-nextjs/.gitignore new file mode 100644 index 0000000000..fd3dbb571a --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/orderbook/create-listing-with-nextjs/README.md b/examples/orderbook/create-listing-with-nextjs/README.md new file mode 100644 index 0000000000..e5afa4917f --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/README.md @@ -0,0 +1,68 @@ +This example application demonstrates how to create a listing using the Immutable SDK. The application connects to the Immutable Sandbox environment and requires a valid client ID and publishable API key (which can be retrieved from the Immutable Hub). + +In order to create a listing, a valid ERC721 or ERC1155 token must be provided. The application will prompt the user to connect their Passport wallet and approve the token. Once the token is approved, a listing with the desired price and quantity is created. + +## Features +- Create a listing using ERC721 token +- Create a listing using ERC1155 token + +## Prerequisites +- Node.js + +## Getting Started +1. Install the dependencies: + +```bash +yarn +``` + +2. Copy the `.env.example` file to `.env`: + +```bash +cp .env.example .env +``` + +3. Replace the `NEXT_PUBLIC_PUBLISHABLE_KEY` and `NEXT_PUBLIC_CLIENT_ID` with your own values from the Immutable Hub. + + +4. Run the development server: + +```bash +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser and you'll be navigated to the home screen. + +## Create listing for ERC721 token +1. Click on the "Create ERC721 Listing" button +2. Connect your Passport wallet +3. Enter the following details + - NFT Contract Address: The contract address of the ERC721 token + - NFT Token ID: The Token ID of the ERC721 token + - Currency Type: The type of currency (Native or ERC20) you'd like to receive for the item + - Currency Amount: The amount of currency you'd like to receive for the item +4. Click on the "Create Listing" button +5. Approve the token for trading +6. Sign the listing +7. If successful, the listing will be created and the order ID will be displayed +8. If unsuccessful, an error message will be displayed + +## Create listing for ERC1155 token +1. Click on the "Create ERC1155 Listing" button +2. Connect your Passport wallet +3. Enter the following details + - NFT Contract Address: The contract address of the ERC1155 token + - NFT Token ID: The Token ID of the ERC1155 token + - NFT Token Quantity: The amount of ERC1155 tokens you'd like to list + - Currency Type: The type of currency (Native or ERC20) you'd like to receive for the item + - Currency Amount: The amount of currency you'd like to receive for the item +4. Click on the "Create Listing" button +5. Approve the token for trading +6. Sign the listing +7. If successful, the listing will be created and the order ID will be displayed +8. If unsuccessful, an error message will be displayed + +## Required Environment Variables + +- NEXT_PUBLIC_PUBLISHABLE_KEY // replace with your publishable API key from Hub +- NEXT_PUBLIC_CLIENT_ID // replace with your client ID from Hub diff --git a/examples/orderbook/create-listing-with-nextjs/next.config.mjs b/examples/orderbook/create-listing-with-nextjs/next.config.mjs new file mode 100644 index 0000000000..4678774e6d --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/examples/orderbook/create-listing-with-nextjs/package.json b/examples/orderbook/create-listing-with-nextjs/package.json new file mode 100644 index 0000000000..89abe95666 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/package.json @@ -0,0 +1,29 @@ +{ + "name": "@examples/create-listing-with-nextjs", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "playwright test" + }, + "dependencies": { + "@biom3/react": "^0.26.1", + "@ethersproject/providers": "^5.7.2", + "@imtbl/sdk": "latest", + "next": "14.2.7", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@playwright/test": "^1.45.3", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.7", + "typescript": "^5" + } +} diff --git a/examples/orderbook/create-listing-with-nextjs/playwright.config.ts b/examples/orderbook/create-listing-with-nextjs/playwright.config.ts new file mode 100644 index 0000000000..0ded5b529b --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/playwright.config.ts @@ -0,0 +1,30 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./tests", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: "html", + + use: { + baseURL: "http://localhost:3000", + trace: "on-first-retry", + }, + + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { name: "webkit", use: { ...devices["Desktop Safari"] } }, + + { name: "Mobile Chrome", use: { ...devices["Pixel 5"] } }, + { name: "Mobile Safari", use: { ...devices["iPhone 12"] } }, + ], + + webServer: { + command: "yarn dev", + url: "http://localhost:3000", + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc1155/page.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc1155/page.tsx new file mode 100644 index 0000000000..53008d4841 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc1155/page.tsx @@ -0,0 +1,345 @@ +"use client"; + +import { useState } from "react"; +import { ethers } from "ethers"; +import { ProviderEvent } from "@imtbl/sdk/passport"; +import { passportInstance } from "../utils/setupPassport"; +import { orderbookSDK } from "../utils/setupOrderbook"; +import { + signAndSubmitApproval, + signListing, + createListing, +} from "@/app/utils/listing"; +import { + Box, + Select, + TextInput, + FormControl, + Heading, + Grid, + Button, + LoadingOverlay, + Link, +} from "@biom3/react"; +import { orderbook } from "@imtbl/sdk"; +import { + ERC1155Item, + NativeItem, + ERC20Item, + PrepareListingParams, +} from "@imtbl/sdk/orderbook"; +import NextLink from "next/link"; + +export default function CreateERC1155ListingWithPassport() { + // setup the accounts state + const [accountsState, setAccountsState] = useState([]); + + // setup the loading state to enable/disable buttons when loading + const [loading, setLoadingState] = useState(false); + + // setup the loading text to display while loading + const [loadingText, setLoadingText] = useState(""); + + // fetch the Passport provider from the Passport instance + const passportProvider = passportInstance.connectEvm(); + + // create the Web3Provider using the Passport provider + const web3Provider = new ethers.providers.Web3Provider(passportProvider); + + // setup the state for the ERC1155 listing creation form elements + + // setup the sell item contract address state + const [sellItemContractAddress, setSellItemContractAddressState] = + useState(""); + + // setup the sell item token ID state + const [sellItemTokenID, setSellItemTokenIDState] = useState(""); + + // setup the sell item quantity state + const [sellItemQty, setSellItemQtyState] = useState(""); + + // setup the buy item type state + const [buyItemType, setBuyItemTypeState] = useState("Native"); + + // setup the show buy item contract address state + const [showBuyItemContractAddress, setShowBuyItemContractAddressState] = + useState(false); + + // setup the buy item contract address state + const [buyItemContractAddress, setBuyItemContractAddressState] = + useState(""); + + // setup the buy item amount state + const [buyItemAmount, setBuyItemAmountState] = useState(""); + + // setup the listing creation success message state + const [successMessage, setSuccessMessageState] = useState(null); + + // setup the listing creation error message state + const [listingError, setListingErrorState] = useState(null); + + const passportLogin = async () => { + if (web3Provider.provider.request) { + // disable button while loading + setLoadingState(true); + setLoadingText("Connecting to Passport"); + + // calling eth_requestAccounts triggers the Passport login flow + const accounts = await web3Provider.provider.request({ + method: "eth_requestAccounts", + }); + + // once logged in Passport is connected to the wallet and ready to transact + setAccountsState(accounts); + // enable button when loading has finished + setLoadingState(false); + } + }; + + // listen to the ACCOUNTS_CHANGED event and update the accounts state when it changes + passportProvider.on(ProviderEvent.ACCOUNTS_CHANGED, (accounts: string[]) => { + setAccountsState(accounts); + }); + + const passportLogout = async () => { + // disable button while loading + setLoadingState(true); + setLoadingText("Logging out"); + // reset the account state + setAccountsState([]); + // logout from passport + await passportInstance.logout(); + }; + + // state change handlers + const handleSellItemContractAddressChange = (event: any) => { + setSellItemContractAddressState(event.target.value); + }; + + const handleSellItemTokenIDChange = (event: any) => { + setSellItemTokenIDState(event.target.value); + }; + + const handleSellItemTokenQtyChange = (event: any) => { + setSellItemQtyState(event.target.value); + }; + + const handleBuyItemTypeChange = (val: any) => { + setBuyItemTypeState(val); + val === "ERC20" + ? setShowBuyItemContractAddressState(true) + : setShowBuyItemContractAddressState(false); + }; + + const handleBuyItemContractAddressChange = (event: any) => { + setBuyItemContractAddressState(event.target.value); + }; + + const handleBuyItemAmountChange = (event: any) => { + setBuyItemAmountState(event.target.value); + }; + + const handleSuccessfulListingCreation = (listingID: string) => { + setSuccessMessageState(`Listing created successfully - ${listingID}`); + }; + + // #doc prepare-erc1155-listing + // prepare ERC1155 listing + const prepareERC1155Listing = + async (): Promise => { + // build the sell item + const sell: ERC1155Item = { + contractAddress: sellItemContractAddress, + tokenId: sellItemTokenID, + amount: sellItemQty, + type: "ERC1155", + }; + + // build the buy item + const buy = + buyItemType === "Native" + ? ({ + amount: buyItemAmount, + type: "NATIVE", + } as NativeItem) + : ({ + amount: buyItemAmount, + type: "ERC20", + contractAddress: buyItemContractAddress, + } as ERC20Item); + + // build the prepare listing parameters + const prepareListingParams: PrepareListingParams = { + makerAddress: accountsState[0], + buy, + sell, + }; + + // invoke the orderbook SDK to prepare the listing + return await orderbookSDK.prepareListing(prepareListingParams); + }; + // #enddoc prepare-erc1155-listing + + // create ERC1155 listing + const createER1155Listing = async () => { + setListingErrorState(null); + + try { + // prepare the listing + const preparedListing = await prepareERC1155Listing(); + + // sign and submit approval transaction + await signAndSubmitApproval(web3Provider, preparedListing); + + // sign the listing + const orderSignature = await signListing(web3Provider, preparedListing); + + // create the listing + const listingID = await createListing( + orderbookSDK, + preparedListing, + orderSignature, + ); + + handleSuccessfulListingCreation(listingID); + } catch (error: any) { + console.error(error); + setSuccessMessageState(null); + setListingErrorState(`Something went wrong - ${error.message}`); + } + }; + + return ( + + + + Passport + + + {accountsState.length === 0 && ( + + + + )} + {accountsState.length >= 1 && ( + + + + )} + {loading ? ( + + + + + + ) : ( + + Connected Account: + {accountsState.length >= 1 ? accountsState : "(not connected)"} + + )} + + + + + Create ERC1155 listing + + {successMessage && ( + + {successMessage} + + )} + {listingError && ( + + {listingError} + + )} + + NFT Contract Address + + + + NFT Token ID + + + + NFT Token Quantity + + + + Currency Type + + + {showBuyItemContractAddress && ( + + Currency Contract Address + + + )} + + Currency Amount + + + + + + + }>Return to Examples + + ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc721/page.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc721/page.tsx new file mode 100644 index 0000000000..48c88b92ff --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/create-listing-with-erc721/page.tsx @@ -0,0 +1,333 @@ +"use client"; + +import { useState } from "react"; +import { ethers } from "ethers"; +import { ProviderEvent } from "@imtbl/sdk/passport"; +import { passportInstance } from "../utils/setupPassport"; +import { orderbookSDK } from "../utils/setupOrderbook"; +import { + signAndSubmitApproval, + signListing, + createListing, +} from "@/app/utils/listing"; +import { + Box, + Select, + TextInput, + FormControl, + Heading, + Grid, + Button, + LoadingOverlay, + Link, +} from "@biom3/react"; +import { orderbook } from "@imtbl/sdk"; +import { + ERC721Item, + NativeItem, + ERC20Item, + PrepareListingParams, +} from "@imtbl/sdk/orderbook"; +import NextLink from "next/link"; + +export default function CreateERC721ListingWithPassport() { + // setup the accounts state + const [accountsState, setAccountsState] = useState([]); + + // setup the loading state to enable/disable buttons when loading + const [loading, setLoadingState] = useState(false); + + // setup the loading text to display while loading + const [loadingText, setLoadingText] = useState(""); + + // fetch the Passport provider from the Passport instance + const passportProvider = passportInstance.connectEvm(); + + // create the Web3Provider using the Passport provider + const web3Provider = new ethers.providers.Web3Provider(passportProvider); + + // setup the state for the ERC721 listing creation form elements + + // setup the sell item contract address state + const [sellItemContractAddress, setSellItemContractAddressState] = + useState(""); + + // setup the sell item token ID state + const [sellItemTokenID, setSellItemTokenIDState] = useState(""); + + // setup the buy item type state + const [buyItemType, setBuyItemTypeState] = useState("Native"); + + // setup the show buy item contract address state + const [showBuyItemContractAddress, setShowBuyItemContractAddressState] = + useState(false); + + // setup the buy item contract address state + const [buyItemContractAddress, setBuyItemContractAddressState] = + useState(""); + + // setup the buy item amount state + const [buyItemAmount, setBuyItemAmountState] = useState(""); + + // setup the listing creation success message state + const [successMessage, setSuccessMessageState] = useState(null); + + // setup the listing creation error message state + const [listingError, setListingErrorState] = useState(null); + + const passportLogin = async () => { + if (web3Provider.provider.request) { + // disable button while loading + setLoadingState(true); + setLoadingText("Connecting to Passport"); + + // calling eth_requestAccounts triggers the Passport login flow + const accounts = await web3Provider.provider.request({ + method: "eth_requestAccounts", + }); + + // once logged in Passport is connected to the wallet and ready to transact + setAccountsState(accounts); + // enable button when loading has finished + setLoadingState(false); + } + }; + + // listen to the ACCOUNTS_CHANGED event and update the accounts state when it changes + passportProvider.on(ProviderEvent.ACCOUNTS_CHANGED, (accounts: string[]) => { + setAccountsState(accounts); + }); + + const passportLogout = async () => { + // disable button while loading + setLoadingState(true); + setLoadingText("Logging out"); + // reset the account state + setAccountsState([]); + // logout from passport + await passportInstance.logout(); + }; + + // state change handlers + const handleSellItemContractAddressChange = (event: any) => { + setSellItemContractAddressState(event.target.value); + }; + + const handleSellItemTokenIDChange = (event: any) => { + setSellItemTokenIDState(event.target.value); + }; + + const handleBuyItemTypeChange = (val: any) => { + setBuyItemTypeState(val); + val === "ERC20" + ? setShowBuyItemContractAddressState(true) + : setShowBuyItemContractAddressState(false); + }; + + const handleBuyItemContractAddressChange = (event: any) => { + setBuyItemContractAddressState(event.target.value); + }; + + const handleBuyItemAmountChange = (event: any) => { + setBuyItemAmountState(event.target.value); + }; + + const handleSuccessfulListingCreation = (listingID: string) => { + setSuccessMessageState(`Listing created successfully - ${listingID}`); + }; + + // #doc prepare-erc721-listing + // prepare ERC721 listing + const prepareERC721Listing = + async (): Promise => { + // build the sell item + const sell: ERC721Item = { + contractAddress: sellItemContractAddress, + tokenId: sellItemTokenID, + type: "ERC721", + }; + + // build the buy item + const buy = + buyItemType === "Native" + ? ({ + amount: buyItemAmount, + type: "NATIVE", + } as NativeItem) + : ({ + amount: buyItemAmount, + type: "ERC20", + contractAddress: buyItemContractAddress, + } as ERC20Item); + + // build the prepare listing parameters + const prepareListingParams: PrepareListingParams = { + makerAddress: accountsState[0], + buy, + sell, + }; + + // invoke the orderbook SDK to prepare the listing + return await orderbookSDK.prepareListing(prepareListingParams); + }; + // #enddoc prepare-erc721-listing + + // create ERC721 listing + const createER721Listing = async () => { + setListingErrorState(null); + + try { + // prepare the listing + const preparedListing = await prepareERC721Listing(); + + // sign and submit approval transaction + await signAndSubmitApproval(web3Provider, preparedListing); + + // sign the listing + const orderSignature = await signListing(web3Provider, preparedListing); + + // create the listing + const listingID = await createListing( + orderbookSDK, + preparedListing, + orderSignature, + ); + + handleSuccessfulListingCreation(listingID); + } catch (error: any) { + console.error(error); + setSuccessMessageState(null); + setListingErrorState(`Something went wrong - ${error.message}`); + } + }; + + return ( + + + + Passport + + + {accountsState.length === 0 && ( + + + + )} + {accountsState.length >= 1 && ( + + + + )} + {loading ? ( + + + + + + ) : ( + + Connected Account: + {accountsState.length >= 1 ? accountsState : "(not connected)"} + + )} + + + + + Create ERC721 listing + + {successMessage && ( + + {successMessage} + + )} + {listingError && ( + + {listingError} + + )} + + NFT Contract Address + + + + NFT Token ID + + + + Currency Type + + + {showBuyItemContractAddress && ( + + Currency Contract Address + + + )} + + Currency Amount + + + + + + + }>Return to Examples + + ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/favicon.ico b/examples/orderbook/create-listing-with-nextjs/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/globals.css b/examples/orderbook/create-listing-with-nextjs/src/app/globals.css new file mode 100644 index 0000000000..22fe767817 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/globals.css @@ -0,0 +1,18 @@ +html, body { + height: 100%; +} +body { + margin: 0; +} +.flex-container { + height: 100%; + padding: 0; + margin: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.mb-1 { + margin-bottom: 1rem; +} \ No newline at end of file diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/layout.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/layout.tsx new file mode 100644 index 0000000000..8c67603737 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; +import AppWrapper from "./utils/wrapper"; +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Orderbook SDK - Create listing with NextJS", + description: + "Examples of how to create a listing using the Orderbook SDK with NextJS", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/logout/page.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/logout/page.tsx new file mode 100644 index 0000000000..87465321cf --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/logout/page.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { Box, Heading, Link } from "@biom3/react"; +import NextLink from "next/link"; + +export default function Logout() { + // render the view for after the logout is complete + return ( + + + Logged out + + }>Return to Examples + + ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/page.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/page.tsx new file mode 100644 index 0000000000..e36b1f092c --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/page.tsx @@ -0,0 +1,29 @@ +"use client"; +import { Button, Heading } from "@biom3/react"; +import NextLink from "next/link"; + +export default function Home() { + return ( + <> + + Orderbook - Create Listing + + + + + ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/redirect/page.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/redirect/page.tsx new file mode 100644 index 0000000000..f4f0c0f1b4 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/redirect/page.tsx @@ -0,0 +1,19 @@ +"use client"; + +import { useEffect } from "react"; +import { passportInstance } from "../utils/setupPassport"; +import { Box, Heading } from "@biom3/react"; + +export default function Redirect() { + useEffect(() => { + // call the loginCallback function after the login is complete + passportInstance.loginCallback(); + }, []); + + // render the view for the login popup after the login is complete + return ( + + Logged in + + ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/utils/listing.ts b/examples/orderbook/create-listing-with-nextjs/src/app/utils/listing.ts new file mode 100644 index 0000000000..d0bb730cc9 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/utils/listing.ts @@ -0,0 +1,75 @@ +import { orderbook } from "@imtbl/sdk"; +import { Web3Provider } from "@ethersproject/providers"; + +// #doc sign-and-submit-approval +export const signAndSubmitApproval = async ( + provider: Web3Provider, + listing: orderbook.PrepareListingResponse, +): Promise => { + // get your user's Web3 wallet, e.g. MetaMask, Passport, etc + const signer = provider.getSigner(); + + // If the user hasn't yet approved the Immutable Seaport contract to transfer assets from this + // collection on their behalf they'll need to do so before they create an order + const approvalAction = listing.actions.find( + (action): action is orderbook.TransactionAction => + action.type === orderbook.ActionType.TRANSACTION, + ); + + if (approvalAction) { + const unsignedTx = await approvalAction.buildTransaction(); + const receipt = await signer.sendTransaction(unsignedTx); + await receipt.wait(); + } + + return; +}; +// #enddoc sign-and-submit-approval + +// #doc sign-listing +export const signListing = async ( + provider: Web3Provider, + listing: orderbook.PrepareListingResponse, +): Promise => { + // get your user's Web3 wallet, e.g. MetaMask, Passport, etc + const signer = provider.getSigner(); + + // For an order to be created (and subsequently filled), Immutable needs a valid signature for the order data. + // This signature is stored off-chain and is later provided to any user wishing to fulfil the open order. + // The signature only allows the order to be fulfilled if it meets the conditions specified by the user that created the listing. + const signableAction = listing.actions.find( + (action): action is orderbook.SignableAction => + action.type === orderbook.ActionType.SIGNABLE, + )!; + + const signature = await signer._signTypedData( + signableAction.message.domain, + signableAction.message.types, + signableAction.message.value, + ); + + return signature; +}; +// #enddoc sign-listing + +// #doc create-listing +export const createListing = async ( + client: orderbook.Orderbook, + preparedListing: orderbook.PrepareListingResponse, + orderSignature: string, +): Promise => { + const order = await client.createListing({ + orderComponents: preparedListing.orderComponents, + orderHash: preparedListing.orderHash, + orderSignature, + // Optional maker marketplace fee + makerFees: [ + { + amount: "100", + recipientAddress: "0xFooBar", // Replace address with your own marketplace address + }, + ], + }); + return order.result.id; +}; +// #enddoc create-listing diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/utils/setupOrderbook.ts b/examples/orderbook/create-listing-with-nextjs/src/app/utils/setupOrderbook.ts new file mode 100644 index 0000000000..e14a5b88ac --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/utils/setupOrderbook.ts @@ -0,0 +1,8 @@ +import { orderbook } from "@imtbl/sdk"; +import { Environment } from "@imtbl/sdk/config"; + +export const orderbookSDK = new orderbook.Orderbook({ + baseConfig: { + environment: Environment.SANDBOX, + }, +}); diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/utils/setupPassport.ts b/examples/orderbook/create-listing-with-nextjs/src/app/utils/setupPassport.ts new file mode 100644 index 0000000000..d6018f9ac0 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/utils/setupPassport.ts @@ -0,0 +1,18 @@ +import { config, passport } from "@imtbl/sdk"; + +// create the Passport instance and export it so it can be used in the examples +export const passportInstance = new passport.Passport({ + baseConfig: { + environment: config.Environment.SANDBOX, + publishableKey: process.env.NEXT_PUBLIC_PUBLISHABLE_KEY ?? "", // replace with your publishable API key from Hub + }, + clientId: process.env.NEXT_PUBLIC_CLIENT_ID ?? "", // replace with your client ID from Hub + redirectUri: "http://localhost:3000/redirect", // replace with one of your redirect URIs from Hub + logoutRedirectUri: "http://localhost:3000/logout", // replace with one of your logout URIs from Hub + audience: "platform_api", + scope: "openid offline_access email transact", + popupOverlayOptions: { + disableGenericPopupOverlay: false, // Set to true to disable the generic pop-up overlay + disableBlockedPopupOverlay: false, // Set to true to disable the blocked pop-up overlay + }, +}); diff --git a/examples/orderbook/create-listing-with-nextjs/src/app/utils/wrapper.tsx b/examples/orderbook/create-listing-with-nextjs/src/app/utils/wrapper.tsx new file mode 100644 index 0000000000..ae2bdafa8b --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/src/app/utils/wrapper.tsx @@ -0,0 +1,16 @@ +"use client"; +import { BiomeCombinedProviders, Stack } from "@biom3/react"; + +export default function AppWrapper({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( +
+ + {children} + +
+ ); +} diff --git a/examples/orderbook/create-listing-with-nextjs/tests/base.spec.ts b/examples/orderbook/create-listing-with-nextjs/tests/base.spec.ts new file mode 100644 index 0000000000..50231764b0 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/tests/base.spec.ts @@ -0,0 +1,28 @@ +import { test, expect } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.goto("/"); +}); + +test.describe("home page", () => { + test("has title, heading and creation links", async ({ page }) => { + await expect(page).toHaveTitle("Orderbook SDK - Create listing with NextJS"); + await expect(page.getByRole("heading", { name: "Orderbook - Create Listing" })).toBeVisible(); + await expect(page.getByTestId("create-listing-with-erc721")).toBeVisible(); + await expect(page.getByTestId("create-listing-with-erc1155")).toBeVisible(); + }); +}); + +test.describe("create listing with ERC721", () => { + test("loads creation screen", async ({ page }) => { + await page.getByTestId("create-listing-with-erc721").click(); + await expect(page.getByRole("heading", { level: 1 })).toBeVisible(); + }); +}); + +test.describe("create listing with ERC1155", () => { + test("loads creation screen", async ({ page }) => { + await page.getByTestId("create-listing-with-erc1155").click(); + await expect(page.getByRole("heading", { level: 1 })).toBeVisible(); + }); +}); diff --git a/examples/orderbook/create-listing-with-nextjs/tsconfig.json b/examples/orderbook/create-listing-with-nextjs/tsconfig.json new file mode 100644 index 0000000000..7b28589304 --- /dev/null +++ b/examples/orderbook/create-listing-with-nextjs/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/package.json b/package.json index 37d137199f..3f9bb70dda 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,8 @@ "packages/webhook/sdk", "packages/minting-backend/sdk", "tests/**", - "examples/passport/**" + "examples/passport/**", + "examples/orderbook/**" ], "nohoist": [ "examples/**", diff --git a/yarn.lock b/yarn.lock index 7dd25620f5..c11b57f445 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2463,6 +2463,15 @@ __metadata: languageName: node linkType: hard +"@biom3/design-tokens@npm:~0.4.4": + version: 0.4.4 + resolution: "@biom3/design-tokens@npm:0.4.4" + dependencies: + lodash.get: ^4.4.2 + checksum: b62db8bf266d41097bc231795eed3208d18e526deff29bfb4d286645ebda34bdb3edf3f4aef7e849b08b4f1af051c801de48e3f250ab1c721b4e5c508113194d + languageName: node + linkType: hard + "@biom3/react@npm:^0.24.1": version: 0.24.20 resolution: "@biom3/react@npm:0.24.20" @@ -2517,6 +2526,33 @@ __metadata: languageName: node linkType: hard +"@biom3/react@npm:^0.26.1": + version: 0.26.2 + resolution: "@biom3/react@npm:0.26.2" + dependencies: + "@biom3/design-tokens": ~0.4.4 + "@imtbl/image-resizer-utils": ~0.0.1 + csstype: ^3.1.2 + hls.js: ^1.5.11 + localforage: ^1.10.0 + lodash.debounce: ^4.0.8 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + react-is: ^18.2.0 + react-keyed-flatten-children: ^3.0.0 + ts-deepmerge: ^6.2.0 + peerDependencies: + "@emotion/react": ^11.11.4 + "@rive-app/react-canvas-lite": ^4.9.0 + embla-carousel-react: ^8.0.4 + framer-motion: ^10.12.12 + react: ^18.2.0 + react-dom: ^18.2.0 + checksum: f70fdcb649e617cc3e9ab5bc093a74f9908a198cd5446370b14dcf3748b1dfacc6c08b517766f41231886bf4ec0dccfe47edb91ba7c7f67a96599cef5e1135bf + languageName: node + linkType: hard + "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -3774,6 +3810,26 @@ __metadata: languageName: node linkType: hard +"@examples/create-listing-with-nextjs@workspace:examples/orderbook/create-listing-with-nextjs": + version: 0.0.0-use.local + resolution: "@examples/create-listing-with-nextjs@workspace:examples/orderbook/create-listing-with-nextjs" + dependencies: + "@biom3/react": ^0.26.1 + "@ethersproject/providers": ^5.7.2 + "@imtbl/sdk": latest + "@playwright/test": ^1.45.3 + "@types/node": ^20 + "@types/react": ^18 + "@types/react-dom": ^18 + eslint: ^8 + eslint-config-next: 14.2.7 + next: 14.2.7 + react: ^18 + react-dom: ^18 + typescript: ^5 + languageName: unknown + linkType: soft + "@examples/identity-with-nextjs@workspace:examples/passport/identity-with-nextjs": version: 0.0.0-use.local resolution: "@examples/identity-with-nextjs@workspace:examples/passport/identity-with-nextjs" @@ -4332,6 +4388,15 @@ __metadata: languageName: unknown linkType: soft +"@imtbl/image-resizer-utils@npm:~0.0.1": + version: 0.0.1 + resolution: "@imtbl/image-resizer-utils@npm:0.0.1" + dependencies: + buffer: ^6.0.3 + checksum: 5094e23136eee70ceb790dbeb71834d25941477bc768fe4d83c5cf4f6b062a68bc3cac110c6585908f44fe13ef0b1a9cb0c1a07042672da90c9713b0b7e9cf98 + languageName: node + linkType: hard + "@imtbl/metrics@0.0.0, @imtbl/metrics@workspace:packages/internal/metrics": version: 0.0.0-use.local resolution: "@imtbl/metrics@workspace:packages/internal/metrics" @@ -6560,6 +6625,13 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:14.2.7": + version: 14.2.7 + resolution: "@next/env@npm:14.2.7" + checksum: 8d3c4338f1a06683f938492391634ce1dd7a5c4a3dff030f4aeddc06fb0feb05904d292af42d5c87c4bb2521c9186c973d6146410a89d1161966b3f6a8d46c75 + languageName: node + linkType: hard + "@next/eslint-plugin-next@npm:13.3.1": version: 13.3.1 resolution: "@next/eslint-plugin-next@npm:13.3.1" @@ -6578,6 +6650,15 @@ __metadata: languageName: node linkType: hard +"@next/eslint-plugin-next@npm:14.2.7": + version: 14.2.7 + resolution: "@next/eslint-plugin-next@npm:14.2.7" + dependencies: + glob: 10.3.10 + checksum: 69115572c2a05a00ceed91c21b42371c8386722e185737458994c6035aaac4dd54af4b2326723cfa4c0ce1103a4cf0c99953a80364ce5bc3957186a08c7a205b + languageName: node + linkType: hard + "@next/eslint-plugin-next@npm:^13.4.7": version: 13.4.9 resolution: "@next/eslint-plugin-next@npm:13.4.9" @@ -6601,6 +6682,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-arm64@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-darwin-arm64@npm:14.2.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-darwin-x64@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-darwin-x64@npm:13.4.11" @@ -6615,6 +6703,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-x64@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-darwin-x64@npm:14.2.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@next/swc-linux-arm64-gnu@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-linux-arm64-gnu@npm:13.4.11" @@ -6629,6 +6724,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-gnu@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.7" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-arm64-musl@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-linux-arm64-musl@npm:13.4.11" @@ -6643,6 +6745,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-musl@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.7" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@next/swc-linux-x64-gnu@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-linux-x64-gnu@npm:13.4.11" @@ -6657,6 +6766,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-gnu@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.7" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-x64-musl@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-linux-x64-musl@npm:13.4.11" @@ -6671,6 +6787,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-musl@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-linux-x64-musl@npm:14.2.7" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@next/swc-win32-arm64-msvc@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-win32-arm64-msvc@npm:13.4.11" @@ -6685,6 +6808,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-arm64-msvc@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.7" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-win32-ia32-msvc@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-win32-ia32-msvc@npm:13.4.11" @@ -6699,6 +6829,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-ia32-msvc@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.7" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@next/swc-win32-x64-msvc@npm:13.4.11": version: 13.4.11 resolution: "@next/swc-win32-x64-msvc@npm:13.4.11" @@ -6713,6 +6850,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-x64-msvc@npm:14.2.7": + version: 14.2.7 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1": version: 5.1.1-v1 resolution: "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1" @@ -12355,6 +12499,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^18": + version: 18.3.0 + resolution: "@types/react-dom@npm:18.3.0" + dependencies: + "@types/react": "*" + checksum: a0cd9b1b815a6abd2a367a9eabdd8df8dd8f13f95897b2f9e1359ea3ac6619f957c1432ece004af7d95e2a7caddbba19faa045f831f32d6263483fc5404a7596 + languageName: node + linkType: hard + "@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.0.11": version: 18.2.7 resolution: "@types/react-dom@npm:18.2.7" @@ -12384,6 +12537,16 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18": + version: 18.3.5 + resolution: "@types/react@npm:18.3.5" + dependencies: + "@types/prop-types": "*" + csstype: ^3.0.2 + checksum: 63d2ff473b348c902b68c20be55d2c5124d078c4336c2d1778f316c27789ed596657e8e714022ce14fb24994b0960fc64c913e629bb0bf85815355b0c31eb46b + languageName: node + linkType: hard + "@types/resolve@npm:1.17.1": version: 1.17.1 resolution: "@types/resolve@npm:1.17.1" @@ -19952,6 +20115,29 @@ __metadata: languageName: node linkType: hard +"eslint-config-next@npm:14.2.7": + version: 14.2.7 + resolution: "eslint-config-next@npm:14.2.7" + dependencies: + "@next/eslint-plugin-next": 14.2.7 + "@rushstack/eslint-patch": ^1.3.3 + "@typescript-eslint/parser": ^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0 + eslint-import-resolver-node: ^0.3.6 + eslint-import-resolver-typescript: ^3.5.2 + eslint-plugin-import: ^2.28.1 + eslint-plugin-jsx-a11y: ^6.7.1 + eslint-plugin-react: ^7.33.2 + eslint-plugin-react-hooks: ^4.5.0 || 5.0.0-canary-7118f5dd7-20230705 + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 + typescript: ">=3.3.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: db8aafac229b4788e002b6df6da0ba747bb14e46c73b997a8dcb962d4c238cca04bd7e0782c2d103847f7d562ba12f85ff8424747e984187349978bf57ee79be + languageName: node + linkType: hard + "eslint-config-react-app@npm:^7.0.1": version: 7.0.1 resolution: "eslint-config-react-app@npm:7.0.1" @@ -29269,6 +29455,64 @@ __metadata: languageName: node linkType: hard +"next@npm:14.2.7": + version: 14.2.7 + resolution: "next@npm:14.2.7" + dependencies: + "@next/env": 14.2.7 + "@next/swc-darwin-arm64": 14.2.7 + "@next/swc-darwin-x64": 14.2.7 + "@next/swc-linux-arm64-gnu": 14.2.7 + "@next/swc-linux-arm64-musl": 14.2.7 + "@next/swc-linux-x64-gnu": 14.2.7 + "@next/swc-linux-x64-musl": 14.2.7 + "@next/swc-win32-arm64-msvc": 14.2.7 + "@next/swc-win32-ia32-msvc": 14.2.7 + "@next/swc-win32-x64-msvc": 14.2.7 + "@swc/helpers": 0.5.5 + busboy: 1.6.0 + caniuse-lite: ^1.0.30001579 + graceful-fs: ^4.2.11 + postcss: 8.4.31 + styled-jsx: 5.1.1 + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: bc0237acb57c338803e0ef5ecc1b51908583da9d9dd325ece8b5862beba27b0ab556e9f8bdea863fc4fbf8378200946dcb0ad911ea978d166c8741c434461fe7 + languageName: node + linkType: hard + "nice-napi@npm:^1.0.2": version: 1.0.2 resolution: "nice-napi@npm:1.0.2" @@ -32859,6 +33103,18 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:^18": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" + dependencies: + loose-envify: ^1.1.0 + scheduler: ^0.23.2 + peerDependencies: + react: ^18.3.1 + checksum: 298954ecd8f78288dcaece05e88b570014d8f6dce5db6f66e6ee91448debeb59dcd31561dddb354eee47e6c1bb234669459060deb238ed0213497146e555a0b9 + languageName: node + linkType: hard + "react-dom@npm:^18.2.0": version: 18.2.0 resolution: "react-dom@npm:18.2.0" @@ -33082,6 +33338,15 @@ __metadata: languageName: node linkType: hard +"react@npm:^18": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: ^1.1.0 + checksum: a27bcfa8ff7c15a1e50244ad0d0c1cb2ad4375eeffefd266a64889beea6f6b64c4966c9b37d14ee32d6c9fcd5aa6ba183b6988167ab4d127d13e7cb5b386a376 + languageName: node + linkType: hard + "react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" @@ -34363,6 +34628,15 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: ^1.1.0 + checksum: 3e82d1f419e240ef6219d794ff29c7ee415fbdc19e038f680a10c067108e06284f1847450a210b29bbaf97b9d8a97ced5f624c31c681248ac84c80d56ad5a2c4 + languageName: node + linkType: hard + "schema-utils@npm:2.7.0": version: 2.7.0 resolution: "schema-utils@npm:2.7.0" @@ -37683,6 +37957,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5": + version: 5.6.2 + resolution: "typescript@npm:5.6.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 48777e1dabd9044519f56cd012b0296e3b72bafe12b7e8e34222751d45c67e0eba5387ecdaa6c14a53871a29361127798df6dc8d1d35643a0a47cb0b1c65a33a + languageName: node + linkType: hard + "typescript@npm:^5.5.4": version: 5.5.4 resolution: "typescript@npm:5.5.4" @@ -37693,6 +37977,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@^5#~builtin": + version: 5.6.2 + resolution: "typescript@patch:typescript@npm%3A5.6.2#~builtin::version=5.6.2&hash=14eedb" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: c084ee1ab865f108c787e6233a5f63c126c482c0c8e87ec998ac5288a2ad54b603e1ea8b8b272355823b833eb31b9fabb99e8c6152283e1cb47e3a76bd6faf6c + languageName: node + linkType: hard + "typescript@patch:typescript@^5.5.4#~builtin": version: 5.5.4 resolution: "typescript@patch:typescript@npm%3A5.5.4#~builtin::version=5.5.4&hash=14eedb"