diff --git a/README.md b/README.md
index 2834a357..888ad781 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
# ⚡ TurboETH - Web3 App Starter Kit
-Web3 App Template built using Next.js, RainbowKit, SIWE, Disco, and more!
+Web3 App Template built using Next.js, RainbowKit, SIWE, SpruceKit, Disco, and more!
### Starter Kit Examples
diff --git a/app/(general)/integration/sprucekit/layout.tsx b/app/(general)/integration/sprucekit/layout.tsx
new file mode 100644
index 00000000..3955cbfb
--- /dev/null
+++ b/app/(general)/integration/sprucekit/layout.tsx
@@ -0,0 +1,9 @@
+'use client'
+
+import { ReactNode } from 'react'
+
+import { SpruceKitProvider } from '@/integrations/sprucekit/spruce-kit-provider'
+
+export default function LayoutIntegration({ children }: { children: ReactNode }) {
+ return {children}
+}
diff --git a/app/(general)/integration/sprucekit/opengraph-image.tsx b/app/(general)/integration/sprucekit/opengraph-image.tsx
new file mode 100644
index 00000000..ff981f67
--- /dev/null
+++ b/app/(general)/integration/sprucekit/opengraph-image.tsx
@@ -0,0 +1,9 @@
+import { IntegrationOgImage } from '@/components/ui/social/og-image-integrations'
+
+export const runtime = 'edge'
+export const size = {
+ width: 1200,
+ height: 630,
+}
+
+export default IntegrationOgImage('sprucekit')
diff --git a/app/(general)/integration/sprucekit/page.tsx b/app/(general)/integration/sprucekit/page.tsx
new file mode 100644
index 00000000..14b19da3
--- /dev/null
+++ b/app/(general)/integration/sprucekit/page.tsx
@@ -0,0 +1,72 @@
+'use client'
+import { motion } from 'framer-motion'
+import Image from 'next/image'
+import Balancer from 'react-wrap-balancer'
+
+import { WalletConnect } from '@/components/blockchain/wallet-connect'
+import { IsDarkTheme } from '@/components/shared/is-dark-theme'
+import { IsLightTheme } from '@/components/shared/is-light-theme'
+import { IsWalletConnected } from '@/components/shared/is-wallet-connected'
+import { IsWalletDisconnected } from '@/components/shared/is-wallet-disconnected'
+import { LinkComponent } from '@/components/shared/link-component'
+import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design'
+import { turboIntegrations } from '@/data/turbo-integrations'
+import { ButtonSpruceKitLogin } from '@/integrations/sprucekit/components/button-sprucekit-login'
+import { ButtonSpruceKitLogout } from '@/integrations/sprucekit/components/button-sprucekit-logout'
+import { IsSignedIn } from '@/integrations/sprucekit/components/is-signed-in'
+import { IsSignedOut } from '@/integrations/sprucekit/components/is-signed-out'
+
+export default function PageIntegration() {
+ return (
+
+
+
+
+
+
+
+
+
+ {turboIntegrations.sprucekit.name}
+
+
+ {turboIntegrations.sprucekit.description}
+
+
+
+ Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/(general)/integration/sprucekit/twitter-image.tsx b/app/(general)/integration/sprucekit/twitter-image.tsx
new file mode 100644
index 00000000..dbca541e
--- /dev/null
+++ b/app/(general)/integration/sprucekit/twitter-image.tsx
@@ -0,0 +1,9 @@
+import Image from './opengraph-image'
+
+export const runtime = 'edge'
+export const size = {
+ width: 1200,
+ height: 630,
+}
+
+export default Image
diff --git a/app/(general)/page.tsx b/app/(general)/page.tsx
index c9b5045e..d15ccd96 100644
--- a/app/(general)/page.tsx
+++ b/app/(general)/page.tsx
@@ -158,6 +158,16 @@ const features = [
),
},
+ {
+ title: 'SpruceKit',
+ description: turboIntegrations.sprucekit.description,
+ href: turboIntegrations.sprucekit.href,
+ demo: (
+
+
+
+ ),
+ },
{
title: 'Rainbowkit',
description: 'The best way to connect a wallet. Designed for everyone. Built for developers.',
diff --git a/app/api/ssx/route.ts b/app/api/ssx/route.ts
new file mode 100644
index 00000000..0eadaa66
--- /dev/null
+++ b/app/api/ssx/route.ts
@@ -0,0 +1 @@
+export { GET } from '@/integrations/sprucekit/api'
diff --git a/app/api/ssx/ssx-login/route.ts b/app/api/ssx/ssx-login/route.ts
new file mode 100644
index 00000000..1276c471
--- /dev/null
+++ b/app/api/ssx/ssx-login/route.ts
@@ -0,0 +1 @@
+export { POST } from '@/integrations/sprucekit/api/login'
diff --git a/app/api/ssx/ssx-logout/route.ts b/app/api/ssx/ssx-logout/route.ts
new file mode 100644
index 00000000..4cacfce4
--- /dev/null
+++ b/app/api/ssx/ssx-logout/route.ts
@@ -0,0 +1 @@
+export { GET } from '@/integrations/sprucekit/api/logout'
diff --git a/app/api/ssx/ssx-nonce/route.ts b/app/api/ssx/ssx-nonce/route.ts
new file mode 100644
index 00000000..7efbac85
--- /dev/null
+++ b/app/api/ssx/ssx-nonce/route.ts
@@ -0,0 +1 @@
+export { GET } from '@/integrations/sprucekit/api/nonce'
diff --git a/app/api/ssx/ssx-verify/route.ts b/app/api/ssx/ssx-verify/route.ts
new file mode 100644
index 00000000..64c5e984
--- /dev/null
+++ b/app/api/ssx/ssx-verify/route.ts
@@ -0,0 +1 @@
+export { POST } from '@/integrations/sprucekit/api/verify'
diff --git a/data/turbo-integrations.ts b/data/turbo-integrations.ts
index 9dc5668a..e4a5497e 100644
--- a/data/turbo-integrations.ts
+++ b/data/turbo-integrations.ts
@@ -7,6 +7,14 @@ export const turboIntegrations = {
imgLight: '/integrations/siwe.svg',
imgDark: '/integrations/siwe.svg',
},
+ sprucekit: {
+ name: 'SpruceKit',
+ href: '/integration/sprucekit',
+ url: 'https://sprucekit.dev/',
+ description: 'The open-source toolkit for decentralized identity.',
+ imgLight: '/integrations/sprucekit.svg',
+ imgDark: '/integrations/sprucekit.svg',
+ },
erc20: {
name: 'ERC20',
href: '/integration/erc20',
diff --git a/integrations/sprucekit/README.md b/integrations/sprucekit/README.md
new file mode 100644
index 00000000..ef833fb4
--- /dev/null
+++ b/integrations/sprucekit/README.md
@@ -0,0 +1,60 @@
+# SpruceKit - TurboETH Integration
+
+Welcome to the [SpruceKit](https://sprucekit.dev/) TurboETH Integration! This integration provides a secure and straightforward method for users to authenticate themselves using their Ethereum wallets.
+
+## Features
+
+- Secure user authentication via their Ethereum wallet.
+- Sign-In with Ethereum (SIWE) login and logout functionality.
+- Displaying user's account details post-authentication.
+- React components to handle various authentication states.
+
+## API
+
+### Actions
+
+`spruceKitLogin()`
+Initiates the SSX login process, creating and signing a SIWE message and then verifying it through a backend service.
+
+`spruceKitLogout()`
+Finalize the SSX session and logs out the user by sending a request to the backend logout service.
+
+### Components
+
+`BranchButtonLoginOrAccount()`
+Renders either a login or logout button and a link to the user's account depending on whether the user is authenticated or not.
+
+`IsSignedInd()`
+A React component that conditionally renders its children if the user is signed in.
+
+`IsSignedOut()`
+A React component that conditionally renders its children if the user is signed out.
+
+`ButtonSpruceKitLogin()`
+A button that initiates the SSX login process when clicked.
+
+`ButtonSpruceKitLogout()`
+A button that initiates the SSX logout process when clicked.
+
+## File Structure
+
+```
+integrations/sprucekit
+├─ actions/
+│ ├─ spruceki-login.ts
+│ ├─ sprucekit-logout.ts
+├─ api/
+│ ├─ _ssx.ts
+│ ├─ index.ts
+│ ├─ login.ts
+│ ├─ logout.ts
+│ ├─ nonce.ts
+│ ├─ verify.ts
+├─ components/
+│ ├─ branch-button-login-or-account.tsx
+│ ├─ button-sprucekit-login.tsx
+│ ├─ button-sprucekit-logout.tsx
+│ ├─ is-signed-in.tsx
+│ ├─ is-signed-out.tsx
+├─ README.md
+```
diff --git a/integrations/sprucekit/actions/sprucekit-login.ts b/integrations/sprucekit/actions/sprucekit-login.ts
new file mode 100644
index 00000000..d7067aec
--- /dev/null
+++ b/integrations/sprucekit/actions/sprucekit-login.ts
@@ -0,0 +1,23 @@
+import { SiweMessage } from 'siwe'
+
+interface SpruceKitLoginProps {
+ message: string
+ signature: string
+}
+
+export const spruceKitLogin = async ({ message, signature }: SpruceKitLoginProps) => {
+ // 1. Verify signature
+ const siweMessage = new SiweMessage(message)
+ const verifyRes = await fetch('/api/ssx/ssx-verify', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ message: siweMessage, signature }),
+ })
+
+ if (!verifyRes.ok) throw new Error('Error verifying message')
+ if (verifyRes.status === 200) {
+ dispatchEvent(new Event('verified'))
+ }
+}
diff --git a/integrations/sprucekit/actions/sprucekit-logout.ts b/integrations/sprucekit/actions/sprucekit-logout.ts
new file mode 100644
index 00000000..bcd3410b
--- /dev/null
+++ b/integrations/sprucekit/actions/sprucekit-logout.ts
@@ -0,0 +1,13 @@
+import axios, { AxiosError } from 'axios'
+
+export async function spruceKitLogout(): Promise {
+ try {
+ await axios.get('/api/ssx/ssx-logout')
+ return true
+ } catch (error) {
+ if (error instanceof AxiosError === true) {
+ return false
+ }
+ throw new Error(`Unexpected Error`)
+ }
+}
diff --git a/integrations/sprucekit/api/_ssx.ts b/integrations/sprucekit/api/_ssx.ts
new file mode 100644
index 00000000..1b1294a0
--- /dev/null
+++ b/integrations/sprucekit/api/_ssx.ts
@@ -0,0 +1,9 @@
+import { SSXServer } from '@spruceid/ssx-server'
+
+import { env } from '@/env.mjs'
+
+const ssx = new SSXServer({
+ signingKey: env.NEXTAUTH_SECRET,
+})
+
+export default ssx
diff --git a/integrations/sprucekit/api/index.ts b/integrations/sprucekit/api/index.ts
new file mode 100644
index 00000000..6a07d70d
--- /dev/null
+++ b/integrations/sprucekit/api/index.ts
@@ -0,0 +1,9 @@
+import { getIronSession } from 'iron-session'
+
+import { SERVER_SESSION_SETTINGS } from '@/lib/session'
+
+export async function GET(req: Request) {
+ const res = new Response()
+ const session = await getIronSession(req, res, SERVER_SESSION_SETTINGS)
+ return new Response(JSON.stringify({ address: session.siwe?.address }))
+}
diff --git a/integrations/sprucekit/api/login.ts b/integrations/sprucekit/api/login.ts
new file mode 100644
index 00000000..49e80ee8
--- /dev/null
+++ b/integrations/sprucekit/api/login.ts
@@ -0,0 +1,66 @@
+import { getIronSession } from 'iron-session'
+import { cookies } from 'next/headers'
+import type { SiweMessage } from 'siwe'
+import { z } from 'zod'
+
+import { env } from '@/env.mjs'
+import { prisma } from '@/lib/prisma'
+import { SERVER_SESSION_SETTINGS } from '@/lib/session'
+
+import ssx from './_ssx'
+
+const admins = env.APP_ADMINS?.split(',') || []
+
+const loginSchema = z.object({
+ siwe: z.string(),
+ signature: z.string(),
+ daoLogin: z.boolean(),
+ resolveEns: z.boolean(),
+ resolveLens: z.boolean(),
+})
+
+interface Session {
+ session: {
+ siwe: SiweMessage
+ }
+}
+
+export async function POST(req: Request) {
+ try {
+ const request = loginSchema.safeParse(await req.json())
+ if (!request.success) {
+ return new Response(JSON.stringify({ ok: false }))
+ }
+ const cookieStore = cookies()
+ const nonce = cookieStore.get('nonce')
+ const { siwe, daoLogin, resolveEns, resolveLens, signature } = request.data
+ const ssxSession: Session = await ssx.login(siwe, signature, daoLogin, resolveEns, nonce?.value ?? '', resolveLens)
+ const res = new Response(JSON.stringify({ ok: true }))
+ const session = await getIronSession(req, res, SERVER_SESSION_SETTINGS)
+ const fields = ssxSession.session.siwe
+ session.siwe = fields
+
+ if (admins.includes(fields.address)) {
+ session.isAdmin = true
+ }
+ await session.save()
+ if (env.DATABASE_URL) {
+ await prisma.user.upsert({
+ where: { id: fields.address },
+ update: {
+ address: fields.address,
+ },
+ create: {
+ id: fields.address,
+ address: fields.address,
+ },
+ })
+ }
+
+ return new Response(JSON.stringify({ ...ssxSession, ok: true }))
+ } catch (e) {
+ const errorMessage = e instanceof Error ? e.message : String(e)
+ console.error(errorMessage)
+ return new Response(JSON.stringify({ ok: false }))
+ }
+}
diff --git a/integrations/sprucekit/api/logout.ts b/integrations/sprucekit/api/logout.ts
new file mode 100644
index 00000000..9e2f9eef
--- /dev/null
+++ b/integrations/sprucekit/api/logout.ts
@@ -0,0 +1,12 @@
+import { getIronSession } from 'iron-session'
+
+import { SERVER_SESSION_SETTINGS } from '@/lib/session'
+
+import ssx from './_ssx'
+
+export async function GET(req: Request) {
+ const res = new Response(JSON.stringify({ ok: (await ssx.logout()) && true }))
+ const session = await getIronSession(req, res, SERVER_SESSION_SETTINGS)
+ session.destroy()
+ return res
+}
diff --git a/integrations/sprucekit/api/nonce.ts b/integrations/sprucekit/api/nonce.ts
new file mode 100644
index 00000000..b8ff355b
--- /dev/null
+++ b/integrations/sprucekit/api/nonce.ts
@@ -0,0 +1,21 @@
+import { getIronSession } from 'iron-session'
+
+import { SERVER_SESSION_SETTINGS } from '@/lib/session'
+
+import ssx from './_ssx'
+
+export async function GET(req: Request) {
+ const nonce = ssx.generateNonce()
+ const res = new Response(nonce, {
+ headers: {
+ 'Content-Type': 'text/plain',
+ 'Set-Cookie': `nonce=${nonce}`,
+ },
+ })
+ const session = await getIronSession(req, res, SERVER_SESSION_SETTINGS)
+ session.destroy()
+ session.nonce = nonce
+ await session.save()
+
+ return res
+}
diff --git a/integrations/sprucekit/api/verify.ts b/integrations/sprucekit/api/verify.ts
new file mode 100644
index 00000000..7c56695b
--- /dev/null
+++ b/integrations/sprucekit/api/verify.ts
@@ -0,0 +1,58 @@
+import { getIronSession } from 'iron-session'
+import { SiweMessage } from 'siwe'
+import { z } from 'zod'
+
+import { env } from '@/env.mjs'
+import { prisma } from '@/lib/prisma'
+import { SERVER_SESSION_SETTINGS } from '@/lib/session'
+
+const admins = env.APP_ADMINS?.split(',') || []
+
+const verifySchema = z.object({
+ signature: z.string(),
+ message: z.object({
+ domain: z.string(),
+ address: z.string(),
+ statement: z.string(),
+ uri: z.string(),
+ version: z.string(),
+ chainId: z.number(),
+ nonce: z.string(),
+ issuedAt: z.string(),
+ }),
+})
+
+export async function POST(req: Request) {
+ try {
+ const res = new Response(JSON.stringify({ ok: true }))
+ const session = await getIronSession(req, res, SERVER_SESSION_SETTINGS)
+ const { message, signature } = verifySchema.parse(await req.json())
+ const siweMessage = new SiweMessage(message)
+ const { data: fields } = await siweMessage.verify({ signature })
+ if (fields.nonce !== session.nonce) return new Response(JSON.stringify({ message: 'Invalid nonce.' }), { status: 422 })
+ session.siwe = fields
+
+ if (admins.includes(fields.address)) {
+ session.isAdmin = true
+ }
+ await session.save()
+ if (env.DATABASE_URL) {
+ await prisma.user.upsert({
+ where: { id: fields.address },
+ update: {
+ address: fields.address,
+ },
+ create: {
+ id: fields.address,
+ address: fields.address,
+ },
+ })
+ }
+
+ return res
+ } catch (e) {
+ const errorMessage = e instanceof Error ? e.message : String(e)
+ console.error(errorMessage)
+ return new Response(JSON.stringify({ ok: false }))
+ }
+}
diff --git a/integrations/sprucekit/components/branch-button-login-or-account.tsx b/integrations/sprucekit/components/branch-button-login-or-account.tsx
new file mode 100644
index 00000000..fe37b9bb
--- /dev/null
+++ b/integrations/sprucekit/components/branch-button-login-or-account.tsx
@@ -0,0 +1,33 @@
+import { IsWalletConnected } from '@/components/shared/is-wallet-connected'
+import { LinkComponent } from '@/components/shared/link-component'
+import { ButtonSpruceKitLogin } from '@/integrations/sprucekit/components/button-sprucekit-login'
+import { ButtonSpruceKitLogout } from '@/integrations/sprucekit/components/button-sprucekit-logout'
+import { cn } from '@/lib/utils'
+
+import { IsSignedIn } from './is-signed-in'
+import { IsSignedOut } from './is-signed-out'
+
+interface BranchButtonLoginOrAccountProps {
+ classNameButtonLogin?: string
+ classNameButtonLogout?: string
+}
+
+export const BranchButtonLoginOrAccount = ({ classNameButtonLogin, classNameButtonLogout }: BranchButtonLoginOrAccountProps) => {
+ return (
+
+
+
+
+
+ Account
+
+
+
+
+
+
+
+ )
+}
+
+export default BranchButtonLoginOrAccount
diff --git a/integrations/sprucekit/components/button-sprucekit-login.tsx b/integrations/sprucekit/components/button-sprucekit-login.tsx
new file mode 100644
index 00000000..4f5b8da2
--- /dev/null
+++ b/integrations/sprucekit/components/button-sprucekit-login.tsx
@@ -0,0 +1,51 @@
+'use client'
+
+import { HTMLAttributes, useState } from 'react'
+
+import { useSSX } from '@spruceid/ssx-react'
+import { useAccount, useNetwork } from 'wagmi'
+
+import { spruceKitLogin } from '@/integrations/sprucekit/actions/sprucekit-login'
+import { useUser } from '@/lib/hooks/use-user'
+import { cn } from '@/lib/utils'
+
+interface ButtonSpruceKitLoginProps extends HTMLAttributes {
+ label?: string
+ disabled?: boolean
+}
+export const ButtonSpruceKitLogin = ({ className, label = 'Sign-In With Ethereum', disabled, children, ...props }: ButtonSpruceKitLoginProps) => {
+ const { mutateUser } = useUser()
+ const { address } = useAccount()
+ const { chain } = useNetwork()
+ const { ssx } = useSSX()
+
+ const [isLoading, setIsLoading] = useState(false)
+
+ const isInvalid = !address || !chain?.id || !ssx
+
+ const handleCreateMessage = async () => {
+ setIsLoading(true)
+ try {
+ if (isInvalid) return
+
+ const { siwe, signature } = await ssx.signIn()
+
+ await spruceKitLogin({ message: siwe, signature })
+ await mutateUser()
+ } catch (error) {
+ console.error(error)
+ }
+ setIsLoading(false)
+ }
+ const classes = cn('relative', className)
+ const labelClasses = cn({
+ 'opacity-0': isLoading,
+ })
+
+ return (
+
+ )
+}
diff --git a/integrations/sprucekit/components/button-sprucekit-logout.tsx b/integrations/sprucekit/components/button-sprucekit-logout.tsx
new file mode 100644
index 00000000..1e691541
--- /dev/null
+++ b/integrations/sprucekit/components/button-sprucekit-logout.tsx
@@ -0,0 +1,31 @@
+'use client'
+
+import { HTMLAttributes } from 'react'
+
+import { useSSX } from '@spruceid/ssx-react'
+
+import { spruceKitLogout } from '@/integrations/sprucekit/actions/sprucekit-logout'
+import { useUser } from '@/lib/hooks/use-user'
+
+interface ButtonSpruceKitLogoutProps extends HTMLAttributes {
+ label?: string
+}
+
+export const ButtonSpruceKitLogout = ({ className, label = 'Logout', children, ...props }: ButtonSpruceKitLogoutProps) => {
+ const { mutateUser } = useUser()
+ const { ssx } = useSSX()
+ const handleLogout = async () => {
+ try {
+ await ssx?.signOut?.()
+ } catch (e) {
+ await spruceKitLogout()
+ }
+ await mutateUser()
+ }
+
+ return (
+
+ )
+}
diff --git a/integrations/sprucekit/components/is-signed-in.tsx b/integrations/sprucekit/components/is-signed-in.tsx
new file mode 100644
index 00000000..af223c55
--- /dev/null
+++ b/integrations/sprucekit/components/is-signed-in.tsx
@@ -0,0 +1,17 @@
+'use client'
+
+import { ReactNode } from 'react'
+
+import { useUser } from '@/lib/hooks/use-user'
+
+interface IsSignedInProps {
+ children: ReactNode
+}
+
+export const IsSignedIn = ({ children }: IsSignedInProps) => {
+ const { user } = useUser()
+
+ if (user?.isLoggedIn) return <>{children}>
+
+ return null
+}
diff --git a/integrations/sprucekit/components/is-signed-out.tsx b/integrations/sprucekit/components/is-signed-out.tsx
new file mode 100644
index 00000000..f20b39db
--- /dev/null
+++ b/integrations/sprucekit/components/is-signed-out.tsx
@@ -0,0 +1,17 @@
+'use client'
+
+import { ReactNode } from 'react'
+
+import { useUser } from '@/lib/hooks/use-user'
+
+interface IsSignedOutProps {
+ children: ReactNode
+}
+
+export const IsSignedOut = ({ children }: IsSignedOutProps) => {
+ const { user } = useUser()
+
+ if (!user?.isLoggedIn) return <>{children}>
+
+ return null
+}
diff --git a/integrations/sprucekit/spruce-kit-provider.tsx b/integrations/sprucekit/spruce-kit-provider.tsx
new file mode 100644
index 00000000..e9170d02
--- /dev/null
+++ b/integrations/sprucekit/spruce-kit-provider.tsx
@@ -0,0 +1,37 @@
+import type { ReactNode } from 'react'
+
+import { SSXProvider } from '@spruceid/ssx-react'
+import { useWalletClient } from 'wagmi'
+
+import { siteConfig } from '@/config/site'
+
+const ssxConfig = {
+ providers: {
+ server: {
+ host: `${window.location.origin}/api/ssx`,
+ routes: {
+ logout: {
+ method: 'get',
+ },
+ },
+ },
+ },
+ siweConfig: {
+ statement: `Sign in with Ethereum to ${siteConfig.name}`,
+ uri: window.location.origin,
+ },
+}
+
+export function SpruceKitProvider({ children }: { children: ReactNode }) {
+ const { data: walletClient } = useWalletClient()
+
+ const web3Provider = {
+ provider: walletClient,
+ }
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/package.json b/package.json
index 52313e4c..14e58365 100644
--- a/package.json
+++ b/package.json
@@ -37,11 +37,11 @@
},
"packageManager": "pnpm@7.1.8",
"dependencies": {
- "@hookform/resolvers": "^3.1.1",
"@connext/nxtp-utils": "^2.0.3",
"@connext/sdk": "2.0.4-alpha.2",
"@gelatonetwork/automate-sdk": "^2.14.0",
"@graphql-typed-document-node/core": "^3.2.0",
+ "@hookform/resolvers": "^3.1.1",
"@lit-protocol/lit-node-client": "2.1.161",
"@livepeer/react": "^2.6.0",
"@prisma/client": "^4.8.1",
@@ -72,6 +72,9 @@
"@radix-ui/react-toast": "^1.1.3",
"@radix-ui/react-tooltip": "^1.0.3",
"@rainbow-me/rainbowkit": "1.0.1",
+ "@spruceid/siwe-parser": "2.0.2",
+ "@spruceid/ssx-react": "^2.0.2",
+ "@spruceid/ssx-server": "^2.0.0",
"@t3-oss/env-nextjs": "^0.4.0",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.2",
@@ -103,7 +106,7 @@
"react-responsive": "^9.0.2",
"react-table": "^7.8.0",
"react-wrap-balancer": "^0.3.0",
- "siwe": "1.1.6",
+ "siwe": "2.1.4",
"tailwind-merge": "^1.8.1",
"tailwindcss": "^3.2.4",
"usehooks-ts": "^2.9.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b4e3cdc7..10ba738b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -106,6 +106,15 @@ dependencies:
'@rainbow-me/rainbowkit':
specifier: 1.0.1
version: 1.0.1(@types/react@18.0.26)(react-dom@18.2.0)(react@18.2.0)(viem@1.0.0)(wagmi@1.1.0)
+ '@spruceid/siwe-parser':
+ specifier: 2.0.2
+ version: 2.0.2
+ '@spruceid/ssx-react':
+ specifier: ^2.0.2
+ version: 2.0.2(wagmi@1.1.0)
+ '@spruceid/ssx-server':
+ specifier: ^2.0.0
+ version: 2.0.0
'@t3-oss/env-nextjs':
specifier: ^0.4.0
version: 0.4.0(typescript@5.0.4)(zod@3.21.4)
@@ -200,8 +209,8 @@ dependencies:
specifier: ^0.3.0
version: 0.3.0(react@18.2.0)
siwe:
- specifier: 1.1.6
- version: 1.1.6(ethers@5.7.2)
+ specifier: 2.1.4
+ version: 2.1.4(ethers@5.7.2)
tailwind-merge:
specifier: ^1.8.1
version: 1.10.0
@@ -6676,6 +6685,11 @@ packages:
- encoding
dev: false
+ /@metamask/detect-provider@1.2.0:
+ resolution: {integrity: sha512-ocA76vt+8D0thgXZ7LxFPyqw3H7988qblgzddTDA6B8a/yU0uKV42QR/DhA+Jh11rJjxW0jKvwb5htA6krNZDQ==}
+ engines: {node: '>= 10'}
+ dev: false
+
/@metamask/eth-sig-util@4.0.1:
resolution: {integrity: sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==}
engines: {node: '>=12.0.0'}
@@ -9111,6 +9125,101 @@ packages:
apg-js: 4.1.3
dev: false
+ /@spruceid/siwe-parser@2.0.2:
+ resolution: {integrity: sha512-9WuA0ios2537cWYu39MMeH0O2KdrMKgKlOBUTWRTXQjCYu5B+mHCA0JkCbFaJ/0EjxoVIcYCXIW/DoPEpw+PqA==}
+ dependencies:
+ '@noble/hashes': 1.3.0
+ apg-js: 4.1.3
+ uri-js: 4.4.1
+ valid-url: 1.0.9
+ dev: false
+
+ /@spruceid/ssx-core@2.0.0(ethers@5.7.2):
+ resolution: {integrity: sha512-c3N+KtEmi9l+8JKW6plJi+YErnKYNwpwop2B5J6Cpt4s9gmYq/rV10A9blo/HLNAukmX17rMy5yGN5MIeWFTug==}
+ engines: {node: '>=18.16.0'}
+ peerDependencies:
+ ethers: ^5.7.2
+ dependencies:
+ '@spruceid/ssx-sdk-wasm': 0.3.0
+ axios: 0.27.2
+ ethers: 5.7.2
+ events: 3.3.0
+ express: 4.18.2
+ express-session: 1.17.3
+ siwe: 2.1.4(ethers@5.7.2)
+ transitivePeerDependencies:
+ - debug
+ - supports-color
+ dev: false
+
+ /@spruceid/ssx-react@2.0.2(wagmi@1.1.0):
+ resolution: {integrity: sha512-otblNSuDmJdPwcsJKXaWbX9OMPWYiGA1RUixynQZ9vrbU0WQix5Mj5h6hJExkWn3J2MyK8FzT/uDy6vteGcUYA==}
+ peerDependencies:
+ wagmi: '>=0.5.0'
+ dependencies:
+ '@spruceid/ssx': 2.1.0
+ wagmi: 1.1.0(react-dom@18.2.0)(react-native@0.72.3)(react@18.2.0)(typescript@5.0.4)(viem@1.0.0)(zod@3.21.4)
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+ dev: false
+
+ /@spruceid/ssx-sdk-wasm@0.3.0:
+ resolution: {integrity: sha512-uuLVoajjgMahhv+i5xPHz/v6I1TMycDW5zmIOE3F29+amDt2q+C14yjFOlr0j625Zm4qiB3O4QbxZEqWKrQ5Hg==}
+ dev: false
+
+ /@spruceid/ssx-server@2.0.0:
+ resolution: {integrity: sha512-yJDF5IJ1wGbDXFfGTIfQ8qoWvcE6IX3p17rYPFnBc7EfNym9Ml3jlq5lo1OpDR7gunrPyQo3AHT4P3HHHRNfAg==}
+ hasBin: true
+ dependencies:
+ '@spruceid/ssx-core': 2.0.0(ethers@5.7.2)
+ axios: 0.27.2
+ body-parser: 1.20.2
+ cookie-parser: 1.4.6
+ cors: 2.8.5
+ ethers: 5.7.2
+ express: 4.18.2
+ express-session: 1.17.3
+ rc: 1.2.8
+ siwe: 2.1.4(ethers@5.7.2)
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+ dev: false
+
+ /@spruceid/ssx@2.1.0:
+ resolution: {integrity: sha512-r5siD/dZk6mEVNoBiVfjOL7G5QWYI5ysITB4hTagJbGqbH4MPqQ6ET13uWJRcFDxnNoxgvVYzz+KPgOVN6ppeg==}
+ dependencies:
+ '@metamask/detect-provider': 1.2.0
+ '@spruceid/ssx-core': 2.0.0(ethers@5.7.2)
+ '@spruceid/ssx-sdk-wasm': 0.3.0
+ assert: 2.0.0
+ axios: 0.27.2
+ browser: 0.2.6
+ buffer: 6.0.3
+ cross-env: 5.0.5
+ ethers: 5.7.2
+ events: 3.3.0
+ https-browserify: 1.0.0
+ lodash.merge: 4.6.2
+ os-browserify: 0.3.0
+ path-browserify: 1.0.1
+ process: 0.11.10
+ siwe: 2.1.4(ethers@5.7.2)
+ stream-browserify: 3.0.0
+ stream-http: 3.2.0
+ url: 0.11.1
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+ dev: false
+
/@stablelib/aead@1.0.1:
resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==}
@@ -11321,6 +11430,15 @@ packages:
engines: {node: '>=0.8'}
dev: false
+ /assert@2.0.0:
+ resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==}
+ dependencies:
+ es6-object-assign: 1.1.0
+ is-nan: 1.3.2
+ object-is: 1.1.5
+ util: 0.12.5
+ dev: false
+
/assertion-error@1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
dev: false
@@ -11421,6 +11539,15 @@ packages:
- debug
dev: false
+ /axios@0.27.2:
+ resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
+ dependencies:
+ follow-redirects: 1.15.2(debug@4.3.4)
+ form-data: 4.0.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
/axios@1.3.3:
resolution: {integrity: sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==}
dependencies:
@@ -11833,7 +11960,6 @@ packages:
/boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
- dev: true
/borsh@0.7.0:
resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==}
@@ -11886,6 +12012,15 @@ packages:
resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
dev: false
+ /browser@0.2.6:
+ resolution: {integrity: sha512-U6FjD1MaSio5jnSbGj7nrMzdy4mHmXe6RZ4/5Oa9CWIEa6iWCwnhJPfbZXGwaOOySqRF10v8BIQ4zYJyhBg97g==}
+ dependencies:
+ cheerio: 1.0.0-rc.12
+ junjo: 0.2.8
+ termcolor: 0.2.0
+ u2r: 0.1.3
+ dev: false
+
/browserify-aes@1.2.0:
resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
dependencies:
@@ -11974,6 +12109,10 @@ packages:
engines: {node: '>=8.0.0'}
dev: false
+ /builtin-status-codes@3.0.0:
+ resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
+ dev: false
+
/bundle-name@3.0.0:
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
engines: {node: '>=12'}
@@ -12236,6 +12375,30 @@ packages:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
dev: false
+ /cheerio-select@2.1.0:
+ resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
+ dependencies:
+ boolbase: 1.0.0
+ css-select: 5.1.0
+ css-what: 6.1.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ dev: false
+
+ /cheerio@1.0.0-rc.12:
+ resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==}
+ engines: {node: '>= 6'}
+ dependencies:
+ cheerio-select: 2.1.0
+ dom-serializer: 2.0.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ htmlparser2: 8.0.2
+ parse5: 7.1.2
+ parse5-htmlparser2-tree-adapter: 7.0.0
+ dev: false
+
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -12598,10 +12761,23 @@ packages:
/convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+ /cookie-parser@1.4.6:
+ resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ cookie: 0.4.1
+ cookie-signature: 1.0.6
+ dev: false
+
/cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
dev: false
+ /cookie@0.4.1:
+ resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
/cookie@0.4.2:
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
engines: {node: '>= 0.6'}
@@ -12739,6 +12915,15 @@ packages:
/create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
+ /cross-env@5.0.5:
+ resolution: {integrity: sha512-pSnNZd+WdVzjhuvHoX5lF+w0fci4yLcwSBA2bF/KnS8U0PkgkAaHs8kOC07ctdLMRk7I76bOAaSnAwXViKUZNA==}
+ engines: {node: '>=4.0'}
+ hasBin: true
+ dependencies:
+ cross-spawn: 5.1.0
+ is-windows: 1.0.2
+ dev: false
+
/cross-fetch@3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
@@ -12754,6 +12939,14 @@ packages:
- encoding
dev: false
+ /cross-spawn@5.1.0:
+ resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
+ dependencies:
+ lru-cache: 4.1.5
+ shebang-command: 1.2.0
+ which: 1.3.1
+ dev: false
+
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -12780,6 +12973,16 @@ packages:
nth-check: 2.1.1
dev: true
+ /css-select@5.1.0:
+ resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.1.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ nth-check: 2.1.1
+ dev: false
+
/css-tree@1.1.3:
resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
engines: {node: '>=8.0.0'}
@@ -12796,7 +12999,6 @@ packages:
/css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
- dev: true
/cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
@@ -12985,6 +13187,11 @@ packages:
which-typed-array: 1.1.9
dev: true
+ /deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+ dev: false
+
/deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
dev: true
@@ -13209,13 +13416,20 @@ packages:
entities: 2.2.0
dev: true
+ /dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.4.0
+ dev: false
+
/dom-walk@0.1.2:
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
dev: false
/domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
- dev: true
/domhandler@4.3.1:
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
@@ -13224,6 +13438,13 @@ packages:
domelementtype: 2.3.0
dev: true
+ /domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+ dependencies:
+ domelementtype: 2.3.0
+ dev: false
+
/domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
dependencies:
@@ -13232,6 +13453,14 @@ packages:
domhandler: 4.3.1
dev: true
+ /domutils@3.1.0:
+ resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ dev: false
+
/dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
dependencies:
@@ -13373,7 +13602,6 @@ packages:
/entities@4.4.0:
resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
engines: {node: '>=0.12'}
- dev: true
/env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
@@ -13509,6 +13737,10 @@ packages:
es6-symbol: 3.1.3
dev: false
+ /es6-object-assign@1.1.0:
+ resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==}
+ dev: false
+
/es6-promise@4.2.8:
resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
@@ -14508,6 +14740,22 @@ packages:
strip-final-newline: 3.0.0
dev: true
+ /express-session@1.17.3:
+ resolution: {integrity: sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ cookie: 0.4.2
+ cookie-signature: 1.0.6
+ debug: 2.6.9
+ depd: 2.0.0
+ on-headers: 1.0.2
+ parseurl: 1.3.3
+ safe-buffer: 5.2.1
+ uid-safe: 2.1.5
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/express@4.18.2:
resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
engines: {node: '>= 0.10.0'}
@@ -15546,6 +15794,15 @@ packages:
resolution: {integrity: sha512-C++tTF1GqkGYecL+2S1wJTfoH6APGAsbb7PAWQ3iVIwgG/EFseAfEVOKFgAFq4yK3+6j1EjUD4UQ9dRJHX/sSQ==}
dev: true
+ /htmlparser2@8.0.2:
+ resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ entities: 4.4.0
+ dev: false
+
/http-cache-semantics@4.1.1:
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
dev: false
@@ -15599,6 +15856,10 @@ packages:
resolve-alpn: 1.2.1
dev: false
+ /https-browserify@1.0.0:
+ resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==}
+ dev: false
+
/https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
@@ -15754,7 +16015,6 @@ packages:
/ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
- dev: true
/inline-style-parser@0.1.1:
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
@@ -16172,6 +16432,14 @@ packages:
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
dev: true
+ /is-nan@1.3.2:
+ resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.2.0
+ dev: false
+
/is-negative-zero@2.0.2:
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
engines: {node: '>= 0.4'}
@@ -16333,7 +16601,6 @@ packages:
/is-windows@1.0.2:
resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
engines: {node: '>=0.10.0'}
- dev: true
/is-wsl@1.1.0:
resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==}
@@ -16873,6 +17140,10 @@ packages:
setimmediate: 1.0.5
dev: false
+ /junjo@0.2.8:
+ resolution: {integrity: sha512-aekTv1Qq5BpOSXWlJWgfTsUKsu1gg/+ZluD+9aJfJcOP/BiywM+8owTX/DIZTp9O7V7YOybOXiIftz35JyjjuA==}
+ dev: false
+
/just-extend@4.2.1:
resolution: {integrity: sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==}
dev: false
@@ -17433,6 +17704,13 @@ packages:
engines: {node: 14 || >=16.14}
dev: true
+ /lru-cache@4.1.5:
+ resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+ dependencies:
+ pseudomap: 1.0.2
+ yallist: 2.1.2
+ dev: false
+
/lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies:
@@ -18823,7 +19101,6 @@ packages:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
dependencies:
boolbase: 1.0.0
- dev: true
/nullthrows@1.1.1:
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
@@ -18865,7 +19142,6 @@ packages:
dependencies:
call-bind: 1.0.2
define-properties: 1.2.0
- dev: true
/object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
@@ -19035,6 +19311,10 @@ packages:
wcwidth: 1.0.1
dev: true
+ /os-browserify@0.3.0:
+ resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==}
+ dev: false
+
/os-tmpdir@1.0.2:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
@@ -19210,6 +19490,19 @@ packages:
lines-and-columns: 1.2.4
dev: true
+ /parse5-htmlparser2-tree-adapter@7.0.0:
+ resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
+ dependencies:
+ domhandler: 5.0.3
+ parse5: 7.1.2
+ dev: false
+
+ /parse5@7.1.2:
+ resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+ dependencies:
+ entities: 4.4.0
+ dev: false
+
/parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
@@ -19223,7 +19516,6 @@ packages:
/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
- dev: true
/path-case@3.0.4:
resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==}
@@ -19720,6 +20012,10 @@ packages:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
dev: false
+ /pseudomap@1.0.2:
+ resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+ dev: false
+
/psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
dev: false
@@ -19733,7 +20029,6 @@ packages:
/punycode@1.4.1:
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
- dev: true
/punycode@2.1.0:
resolution: {integrity: sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==}
@@ -19883,6 +20178,11 @@ packages:
- supports-color
dev: false
+ /random-bytes@1.0.0:
+ resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==}
+ engines: {node: '>= 0.8'}
+ dev: false
+
/randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
dependencies:
@@ -19912,6 +20212,16 @@ packages:
unpipe: 1.0.0
dev: false
+ /rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+ dev: false
+
/react-copy-to-clipboard@5.1.0(react@18.2.0):
resolution: {integrity: sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==}
peerDependencies:
@@ -20804,12 +21114,24 @@ packages:
resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
dev: false
+ /shebang-command@1.2.0:
+ resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ shebang-regex: 1.0.0
+ dev: false
+
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
dependencies:
shebang-regex: 3.0.0
+ /shebang-regex@1.0.0:
+ resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
@@ -20892,6 +21214,18 @@ packages:
ethers: 5.7.2
dev: false
+ /siwe@2.1.4(ethers@5.7.2):
+ resolution: {integrity: sha512-Dke1Qqa3mgiLm3vjqw/+SQ7dl8WV/Pfk3AlQBF94cBFydTYhztngqYrikzE3X5UTsJ6565dfVbQptszsuYZNYg==}
+ peerDependencies:
+ ethers: ^5.6.8 || ^6.0.8
+ dependencies:
+ '@spruceid/siwe-parser': 2.0.2
+ '@stablelib/random': 1.0.2
+ ethers: 5.7.2
+ uri-js: 4.4.1
+ valid-url: 1.0.9
+ dev: false
+
/slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -21107,6 +21441,15 @@ packages:
inherits: 2.0.4
readable-stream: 3.6.2
+ /stream-http@3.2.0:
+ resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==}
+ dependencies:
+ builtin-status-codes: 3.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ xtend: 4.0.2
+ dev: false
+
/stream-shift@1.0.1:
resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
@@ -21250,6 +21593,11 @@ packages:
min-indent: 1.0.1
dev: true
+ /strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@@ -21431,6 +21779,10 @@ packages:
dependencies:
rimraf: 2.6.3
+ /termcolor@0.2.0:
+ resolution: {integrity: sha512-BJ/FFGl0cQAyYYmUurkqQUJA9RyD2PFt9YNd24nlt7HkeuZqUmHL86ELvaY6JY/TLUvmxzJY9/fHHk3jFibUSg==}
+ dev: false
+
/terser@5.19.2:
resolution: {integrity: sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==}
engines: {node: '>=10'}
@@ -21787,6 +22139,10 @@ packages:
engines: {node: '>=12.20'}
hasBin: true
+ /u2r@0.1.3:
+ resolution: {integrity: sha512-OqMjkw3si8wJyuK6RzBQCuJjbDmCm7LEBoQEMrZg1hUtwXBhZxZzEnxUF/Mky3zesSPfwRud5xbvoH7lzoiS3Q==}
+ dev: false
+
/ua-parser-js@1.0.35:
resolution: {integrity: sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==}
dev: true
@@ -21800,6 +22156,13 @@ packages:
commander: 2.13.0
source-map: 0.6.1
+ /uid-safe@2.1.5:
+ resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ random-bytes: 1.0.0
+ dev: false
+
/uint8arraylist@2.4.3:
resolution: {integrity: sha512-oEVZr4/GrH87K0kjNce6z8pSCzLEPqHNLNR5sj8cJOySrTP8Vb/pMIbZKLJGhQKxm1TiZ31atNrpn820Pyqpow==}
engines: {node: '>=16.0.0', npm: '>=7.0.0'}
@@ -21985,6 +22348,13 @@ packages:
resolution: {integrity: sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==}
dev: false
+ /url@0.11.1:
+ resolution: {integrity: sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==}
+ dependencies:
+ punycode: 1.4.1
+ qs: 6.11.1
+ dev: false
+
/urlpattern-polyfill@8.0.2:
resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==}
dev: true
@@ -22114,6 +22484,10 @@ packages:
/v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
+ /valid-url@1.0.9:
+ resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==}
+ dev: false
+
/validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:
@@ -22643,6 +23017,13 @@ packages:
has-tostringtag: 1.0.0
is-typed-array: 1.1.10
+ /which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: false
+
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -22824,6 +23205,10 @@ packages:
engines: {node: '>=0.10.32'}
dev: false
+ /yallist@2.1.2:
+ resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
+ dev: false
+
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
diff --git a/public/integrations/sprucekit.svg b/public/integrations/sprucekit.svg
new file mode 100644
index 00000000..a5c079dc
--- /dev/null
+++ b/public/integrations/sprucekit.svg
@@ -0,0 +1,5 @@
+