diff --git a/.gitignore b/.gitignore index 00703a3..0343544 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ snfoundry_cache/ .env accounts/ .snfoundry_cache/ -coverage/ \ No newline at end of file +coverage/ +node_modules/ +dist/ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..4bf964e --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +*.png diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..60a4939 --- /dev/null +++ b/app/README.md @@ -0,0 +1,114 @@ +# Add a Beast + +Self-service web app for adding a species to the bestiary. Upload pixel art, +name it, choose where it can be minted — one transaction, no permission needed. + +```bash +pnpm install +pnpm build:sdk # the app consumes the SDK's build output +pnpm dev:app +``` + +## Configuration + +Defaults point at the Sepolia deployment in `docs/sepolia-v3-deployment.md`. +Override with a `.env`: + +``` +VITE_STARKNET_RPC_URL=https://api.cartridge.gg/x/starknet/sepolia +VITE_BEASTS_NFT=0x... +VITE_BEASTS_REGISTRY=0x... +``` + +## Pages + +Hash routing, so every page is a real shareable URL on a static host. + +| URL | | +|---|---| +| `#/` | Add a Beast | +| `#/collection/0xabc…` | Every Beast an address holds. **Swap the address to view any wallet** — no connection needed | +| `#/beasts` | The bestiary, one card per species. The original 75 are behind a toggle | +| `#/beasts/76` | Every Beast of one species, strongest first | +| `#/manage` | Species you control | +| `#/manage/76` | Per-species controls | + +Grouping the bestiary by species is the default because the collection is +unbounded and a single species can hold 1,243 Beasts — a flat list of every +token would be unreadable long before it was useful. + +Species pages read the contract's own per-species rank list rather than +scanning, so the ordering shown is the one the collection itself uses. + +## What the app does + +**Register.** Four art variants are required — which one a Beast shows is +decided by the shiny and animated bits in its token ID, so a species cannot ship +a partial set. Art is validated client-side against the same rules the contract +enforces (media type, base64 structure, PNG/GIF magic bytes) *and* decoded by +the browser, because a file that will not decode here renders broken for every +holder. Registration mints the artist's Genesis Beast in the same transaction. + +**Manage.** Lists every species the connected wallet holds the artist role for, +with its live art, and opens the per-species controls: replace art, rotate or +pause the minter, lock either one permanently, opt into kill stats, transfer +the artist role, or graduate to a custom art provider. + +The list comes from the wallet's own tokens: the collection exposes +`token_of_owner_by_index`, and a Genesis Beast is any token with no affixes, so +the species a wallet controls fall straight out of a local decode. That works +because the **artist role is ownership of the Genesis Beast** — there is no +separate role to drift from the token. Everything is artist-only on-chain; the +UI hides what the caller cannot do and the contract is the real gate. + +Transferring a species means sending its Genesis Beast, which the dashboard +spells out before it asks for a signature. + +Art thumbnails come from each species' own provider, which for a custom +provider is arbitrary contract code. One that reverts costs its own thumbnail, +not the whole list. + +## Art loading + +Art is fetched per Beast, not per species. A community provider receives the +whole decoded Beast, so it may legitimately vary art by affix, tier or level — +caching by species would show the wrong picture for exactly the providers that +make the interface worth having. Requests are batched and the cache is keyed by +token ID, shared across pages. + +The contract validates art structurally — media type, base64, magic bytes — but +cannot prove a payload decodes. A tile whose art will not render says so rather +than showing a broken-image icon. + +## The preview card is an approximation + +`CardPreview` mirrors the layout of the on-chain SVG so an artist can judge +their work in context before paying for a transaction. It is **not** the +contract's renderer — that lives in `src/beast_svg.cairo` and only produces +output once a species exists. Porting it byte-for-byte to TypeScript would let +the preview be exact; until then, treat the card as indicative and `token_uri` +as the truth. + +## Session policy + +The Cartridge connector pre-approves the registry's *management* entrypoints, so +an artist signs once and can then iterate without a popup per change. +`register_beast_with_art` is deliberately excluded: it mints a provenance token +and permanently assigns a species ID, so it should always be an explicit +signature. + +## Smoke test + +`scripts/smoke.mjs` drives the built app in headless Chromium against the live +deployment — it checks the chain read renders, name validation rejects an +injection attempt, the preview derives power correctly, and the dashboard loads +a real species. + +```bash +pnpm build && pnpm preview & +node scripts/smoke.mjs +``` + +It needs a Playwright browser; in this dev container that means +`LD_LIBRARY_PATH=/workspace/.playwright-libs/root/usr/lib/x86_64-linux-gnu` and +an `executablePath` pointing at the cached Chromium. diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..6d78a75 --- /dev/null +++ b/app/index.html @@ -0,0 +1,12 @@ + + + + + + Add a Beast — onchain bestiary + + +
+ + + diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..0bd5e2d --- /dev/null +++ b/app/package.json @@ -0,0 +1,30 @@ +{ + "name": "@provable-games/beasts-app", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@cartridge/connector": "^0.13.12", + "@cartridge/controller": "^0.13.12", + "@provable-games/beasts-sdk": "workspace:*", + "@starknet-react/chains": "5.0.3", + "@starknet-react/core": "^5.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "starknet": "9.4.2" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.3.0", + "playwright": "^1.62.1", + "typescript": "^5.8.3", + "vite": "^5.4.0" + } +} diff --git a/app/scripts/smoke.mjs b/app/scripts/smoke.mjs new file mode 100644 index 0000000..c5bfaad --- /dev/null +++ b/app/scripts/smoke.mjs @@ -0,0 +1,83 @@ +import { chromium } from 'playwright'; +import { mkdtempSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +// 32x32 PNG: magenta border on all four sides, green corner-to-corner +// diagonal. Real Beast art is this size, and the border is the tell — if the +// preview crops, an edge goes missing. +const TEST_PNG = + 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAiklEQVR4nLXNSw6AIAyEYQ7B2vvfEk1sULFAHzOZf9FVv9JKoyar9WAk76+LZDwAyfgADGME4IYCYA0dABpTAGWsAIixAfLGHkgaJiBjWIGw4QBihg8IGG7Aa0QAlxEE7EYcMBopwGJkga0BANYGBlgYMGBmIAHVAAN/Aw8MBgV4GyygG0TgNmTUTqHjAu38soSbAAAAAElFTkSuQmCC'; + +const fixtureDir = mkdtempSync(join(tmpdir(), 'beasts-smoke-')); +const pngPath = join(fixtureDir, 'test.png'); +writeFileSync(pngPath, Buffer.from(TEST_PNG, 'base64')); + +const browser = await chromium.launch({ + executablePath: + '/home/ubuntu/.cache/ms-playwright/chromium_headless_shell-1228/chrome-headless-shell-linux64/chrome-headless-shell', +}); +const page = await browser.newPage({ viewport: { width: 1280, height: 1400 } }); +const errors = []; +page.on('console', (m) => { if (m.type() === 'error') errors.push(m.text()); }); +page.on('pageerror', (e) => errors.push(`pageerror: ${e.message}`)); + +await page.goto('http://localhost:4173/', { waitUntil: 'domcontentloaded' }); +await page.waitForTimeout(5000); + +console.log('H1:', await page.textContent('h1')); +const body = await page.textContent('body'); +const m = body.match(/(\d+) species in the bestiary so far/); +console.log('LIVE CHAIN READ:', m ? m[0] : 'FAILED — no species count rendered'); +console.log('art slots:', await page.locator('.art-slot').count()); +console.log('preview card:', await page.locator('.card').count()); + +// Client-side name validation, no wallet needed. +await page.fill('input[placeholder="Gloomfang"]', 'bad"name'); +await page.waitForTimeout(200); +console.log('rejects injection name:', JSON.stringify(await page.locator('.field__error').first().textContent())); + +await page.fill('input[placeholder="Gloomfang"]', 'Gloomfang'); +await page.waitForTimeout(200); +console.log('accepts valid name — card title:', JSON.stringify(await page.textContent('.card__title'))); + +// Tier drives the power number on the preview card. +await page.selectOption('select >> nth=1', '1'); +await page.waitForTimeout(200); +const power = await page.locator('.stat', { hasText: 'Power' }).textContent(); +console.log('tier 1 power (level 10 x 5):', JSON.stringify(power)); + +// Preview sizing. A percentage height against an `aspect-ratio` parent +// resolves to `auto`, so a square Beast used to size itself from its own 1:1 +// ratio, overflow the wider frame, and get cropped by `overflow: hidden`. +await page.locator('.art-slot__drop input[type=file]').first().setInputFiles(pngPath); +await page.waitForTimeout(1200); +const artBox = await page.locator('.card__art').boundingBox(); +const imgBox = await page.locator('.card__art img').boundingBox(); +console.log('preview art fits its frame:', + imgBox.height <= artBox.height + 1 && imgBox.width <= artBox.width + 1, + `(img ${Math.round(imgBox.width)}x${Math.round(imgBox.height)} in frame ${Math.round(artBox.width)}x${Math.round(artBox.height)})`); + +await page.screenshot({ path: 'smoke-register.png', fullPage: true }); + +// Manage lists the species the connected wallet controls, so with no wallet +// it has to ask for one rather than opening an empty page. +await page.click('button:has-text("Manage")'); +await page.waitForTimeout(800); +console.log('Manage without a wallet prompts connect:', + (await page.locator('.modal').count()) > 0 && (await page.locator('.mine').count()) === 0); + +// Wallet picker: both wallet families must be offered, and a wallet whose +// extension is absent must say so rather than fail silently. +const wallets = await page.locator('.wallet__name').allTextContents(); +const states = await page.locator('.wallet__state').allTextContents(); +console.log('wallets offered:', wallets.map((w, i) => `${w}${states[i] ? ` (${states[i]})` : ''}`)); +// A centred fixed overlay: narrow, and vertically centred in the viewport +// whatever the page scroll position. +const box = await page.locator('.modal').boundingBox(); +const vh = page.viewportSize().height; +const centred = Math.abs(box.y + box.height / 2 - vh / 2) < 40; +console.log('modal is a centred overlay:', box.width <= 400 && centred); + +console.log('CONSOLE ERRORS:', errors.length ? errors.slice(0, 4) : 'none'); +await browser.close(); diff --git a/app/src/App.tsx b/app/src/App.tsx new file mode 100644 index 0000000..cc6df82 --- /dev/null +++ b/app/src/App.tsx @@ -0,0 +1,223 @@ +import { useAccount, useDisconnect } from '@starknet-react/core'; +import { BeastsClient, type BeastDefinition } from '@provable-games/beasts-sdk'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { ADDRESSES, provider } from './lib/chain'; +import { href, useRoute } from './lib/router'; +import { Collection } from './components/Collection'; +import { ConnectModal } from './components/ConnectModal'; +import { Dashboard } from './components/Dashboard'; +import { MySpecies } from './components/MySpecies'; +import { RegisterForm, type RegistrationInput } from './components/RegisterForm'; +import { SpeciesCollection } from './components/SpeciesCollection'; +import { SpeciesIndex } from './components/SpeciesIndex'; + +export function App() { + const { address, account } = useAccount(); + const { disconnect } = useDisconnect(); + const [route, navigate] = useRoute(); + + const [connecting, setConnecting] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(); + const [definition, setDefinition] = useState(null); + const [speciesCount, setSpeciesCount] = useState(null); + + const client = useMemo(() => new BeastsClient(provider(), ADDRESSES, account), [account]); + + const refresh = useCallback(async () => { + setSpeciesCount(await client.speciesCount().catch(() => null)); + if (route.name !== 'manage-species') { + setDefinition(null); + return; + } + try { + setDefinition(await client.getDefinition(route.beastId)); + setError(undefined); + } catch { + setDefinition(null); + setError(`Species ${route.beastId} is not registered`); + } + }, [client, route]); + + useEffect(() => { + void refresh(); + }, [refresh]); + + async function register(input: RegistrationInput) { + setSubmitting(true); + setError(undefined); + try { + const before = await client.speciesCount(); + await client.execute( + client.registerWithArtCall({ + name: input.name, + beastType: input.beastType, + tier: input.tier, + minter: input.minter, + art: input.art, + }), + ); + // species_count counts genesis too, so the new ID is the old count + 1. + navigate({ name: 'manage-species', beastId: before + 1n }); + } catch (e) { + setError(e instanceof Error ? e.message : String(e)); + } finally { + setSubmitting(false); + } + } + + const isArtist = !!address && !!definition && BigInt(definition.artist) === BigInt(address); + + function openMine() { + if (address) navigate({ name: 'manage' }); + else setConnecting(true); + } + + function openMyCollection() { + if (address) navigate({ name: 'collection', address }); + else setConnecting(true); + } + + return ( +
+
+ + Add a Beast + + + +
+ +
+ {route.name === 'register' && ( + <> +
+

Put your Beast onchain

+

+ Anyone can add a species to the bestiary. Upload pixel art, name it, + pick where it lives — one transaction, no permission needed. You keep + the Genesis Beast: the creator's token, one per species, forever. +

+ {speciesCount !== null && ( +

+ {speciesCount.toString()} species in the bestiary so far. +

+ )} +
+ + + )} + + {route.name === 'collection' && ( + navigate({ name: 'species', beastId })} + onViewAddress={(next) => navigate({ name: 'collection', address: next })} + /> + )} + + {route.name === 'species-index' && ( + navigate({ name: 'species', beastId })} + /> + )} + + {route.name === 'species' && ( + navigate({ name: 'species-index' })} + onViewOwner={(owner) => navigate({ name: 'collection', address: owner })} + /> + )} + + {route.name === 'manage' && + (address ? ( + navigate({ name: 'manage-species', beastId })} + onRegister={() => navigate({ name: 'register' })} + /> + ) : ( +
+

Connect a wallet to see the species you control.

+ +
+ ))} + + {route.name === 'manage-species' && + (definition ? ( + <> + + void refresh()} + /> + + ) : ( +
+

{error ?? 'Loading…'}

+ + Browse the bestiary + +
+ ))} +
+ +
+ + Sepolia · Registry {shortAddr(ADDRESSES.registry)} · Collection{' '} + {shortAddr(ADDRESSES.nft)} + +
+ + {connecting && setConnecting(false)} />} +
+ ); +} + +/** Addresses differ in padding and case, so compare numerically. */ +function safeEquals(a: string, b: string): boolean { + try { + return BigInt(a) === BigInt(b); + } catch { + return false; + } +} + +function shortAddr(address: string): string { + const hex = BigInt(address).toString(16); + return `0x${hex.slice(0, 6)}…${hex.slice(-4)}`; +} diff --git a/app/src/components/ArtUpload.tsx b/app/src/components/ArtUpload.tsx new file mode 100644 index 0000000..44b9df2 --- /dev/null +++ b/app/src/components/ArtUpload.tsx @@ -0,0 +1,74 @@ +import { useState } from 'react'; +import { ART_SLOTS, ArtLoadError, loadArtFile, type ArtSlot, type LoadedArt } from '../lib/art'; + +interface Props { + loaded: Partial>; + onChange: (slot: ArtSlot, art: LoadedArt | undefined) => void; + onSelect: (slot: ArtSlot) => void; + selected: ArtSlot; + disabled?: boolean; +} + +export function ArtUpload({ loaded, onChange, onSelect, selected, disabled }: Props) { + const [errors, setErrors] = useState>>({}); + + async function handleFile(slot: ArtSlot, kind: 'png' | 'gif', file: File | undefined) { + if (!file) return; + setErrors((e) => ({ ...e, [slot]: undefined })); + try { + const art = await loadArtFile(file, kind); + onChange(slot, art); + onSelect(slot); + } catch (error) { + const message = + error instanceof ArtLoadError ? error.message : 'Could not process that file'; + setErrors((e) => ({ ...e, [slot]: message })); + onChange(slot, undefined); + } + } + + return ( +
+ {ART_SLOTS.map(({ slot, label, hint, kind, accept }) => { + const art = loaded[slot]; + const error = errors[slot]; + return ( +
art && onSelect(slot)} + > +
+ {label} + {kind.toUpperCase()} +
+ + + +

{hint}

+ {art && ( +

+ {(art.bytes / 1024).toFixed(1)} KB · {art.slots.toLocaleString()} slots +

+ )} + {error &&

{error}

} +
+ ); + })} +
+ ); +} diff --git a/app/src/components/BeastTile.tsx b/app/src/components/BeastTile.tsx new file mode 100644 index 0000000..15be85a --- /dev/null +++ b/app/src/components/BeastTile.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react'; +import { + BEAST_TYPE_NAMES, + type Beast, + beastPower, + fullBeastName, + isGenesis, +} from '@provable-games/beasts-sdk'; + +interface Props { + beast: Beast; + speciesName: string; + /** `undefined` while loading, `null` when the provider could not be read. */ + art: string | null | undefined; + rank?: number; + onClick?: () => void; + subtitle?: string; +} + +/** + * One Beast, as a tile. Everything shown except the species name and the art + * comes straight out of the token ID. + */ +export function BeastTile({ beast, speciesName, art, rank, onClick, subtitle }: Props) { + const genesis = isGenesis(beast); + // The contract validates art structurally — media type, base64, magic bytes + // — but cannot prove the payload decodes. A species whose art is malformed + // must degrade to a note, not a browser's broken-image icon. + const [decodeFailed, setDecodeFailed] = useState(false); + const body = ( + <> +
+ {art === undefined ? ( + + ) : art === null || decodeFailed ? ( + + {decodeFailed ? 'Art will not render' : 'Art unavailable'} + + ) : ( + {speciesName} setDecodeFailed(true)} /> + )} + {genesis && Genesis} + {beast.animated === 1 && GIF} +
+ +
+ {fullBeastName(beast, speciesName)} +
+ +
+ {subtitle ?? + `${BEAST_TYPE_NAMES[beast.beastType]} · T${beast.tier} · Power ${beastPower(beast)}`} +
+ +
+ Lv {beast.level} + HP {beast.health} + {/* Genesis Beasts sit outside the ranked list, so rank 0 is "unranked", + not "first". */} + {genesis ? 'Unranked' : rank ? `Rank ${rank}` : ''} +
+ + ); + + return onClick ? ( + + ) : ( +
{body}
+ ); +} diff --git a/app/src/components/CardPreview.tsx b/app/src/components/CardPreview.tsx new file mode 100644 index 0000000..91dde9d --- /dev/null +++ b/app/src/components/CardPreview.tsx @@ -0,0 +1,84 @@ +import { BEAST_TYPE_NAMES, BeastType, prefixName, suffixName } from '@provable-games/beasts-sdk'; + +interface Props { + name: string; + tier: number; + beastType: BeastType; + art?: string; + shiny: boolean; + /** Sample affixes, so the artist can see how a named variant reads. */ + prefix: number; + suffix: number; + level: number; + health: number; +} + +/** + * Approximation of the card the contract renders. + * + * This is NOT the on-chain SVG — that is built by `src/beast_svg.cairo` and + * only exists once a species is registered. This preview mirrors its layout + * and the values it derives so an artist can judge their art in context + * before paying for a transaction. After registration the app shows the real + * `token_uri` output instead. + */ +export function CardPreview({ + name, + tier, + beastType, + art, + shiny, + prefix, + suffix, + level, + health, +}: Props) { + const power = level * (6 - tier); + const displayName = + prefix > 0 && suffix > 0 + ? `"${prefixName(prefix)} ${suffixName(suffix)}" ${name || 'Unnamed'}` + : name || 'Unnamed'; + + return ( +
+
+ {displayName} +
+ +
+ {art ? ( + {`${name} + ) : ( +
Upload art to preview
+ )} +
+ +
+ + + +
+ +
+ + + +
+ +

+ Preview only — the collection renders this card fully onchain. + {prefix > 0 && ' Affixes shown are a sample: every mint draws its own.'}{' '} + Ranks and kill stats appear once a Beast is minted. +

+
+ ); +} + +function Stat({ label, value }: { label: string; value: string }) { + return ( +
+
{label}
+
{value}
+
+ ); +} diff --git a/app/src/components/Collection.tsx b/app/src/components/Collection.tsx new file mode 100644 index 0000000..81da932 --- /dev/null +++ b/app/src/components/Collection.tsx @@ -0,0 +1,128 @@ +import { type Beast, type BeastsClient, isGenesis } from '@provable-games/beasts-sdk'; +import { useEffect, useMemo, useState } from 'react'; +import { artFor, useArt } from '../lib/useArt'; +import { BeastTile } from './BeastTile'; + +interface Props { + client: BeastsClient; + address: string; + isYou: boolean; + onOpenSpecies: (beastId: bigint) => void; + onViewAddress: (address: string) => void; +} + +/** + * Every Beast an address holds. + * + * The address is a route parameter, not component state, so the URL can be + * edited to any wallet and shared — viewing someone else's collection needs no + * connection at all. + */ +export function Collection({ client, address, isYou, onOpenSpecies, onViewAddress }: Props) { + const [beasts, setBeasts] = useState(null); + const [names, setNames] = useState>(new Map()); + const [error, setError] = useState(null); + const [lookup, setLookup] = useState(''); + + useEffect(() => { + let cancelled = false; + setBeasts(null); + setError(null); + + async function load() { + try { + const held = await client.getTokensOfOwner(address); + if (cancelled) return; + setBeasts(held); + + // One name lookup per distinct species, not per token. + const species = [...new Set(held.map((b) => b.id.toString()))]; + const resolved = new Map(); + for (const id of species) { + try { + resolved.set(id, await client.getSpeciesName(BigInt(id))); + } catch { + resolved.set(id, `Species ${id}`); + } + } + if (!cancelled) setNames(resolved); + } catch (e) { + if (!cancelled) setError(e instanceof Error ? e.message : String(e)); + } + } + + void load(); + return () => { + cancelled = true; + }; + }, [client, address]); + + const art = useArt(client, beasts ?? []); + + const sorted = useMemo( + () => + [...(beasts ?? [])].sort( + (a, b) => Number(a.id - b.id) || a.prefix - b.prefix || a.suffix - b.suffix, + ), + [beasts], + ); + + const genesisCount = sorted.filter(isGenesis).length; + + return ( +
+
+
+

{isYou ? 'Your collection' : 'Collection'}

+

+ {address} +

+ {beasts && ( +

+ {beasts.length} {beasts.length === 1 ? 'Beast' : 'Beasts'} + {genesisCount > 0 && + ` · ${genesisCount} Genesis ${genesisCount === 1 ? 'Beast' : 'Beasts'}, so ${genesisCount === 1 ? 'one species' : `${genesisCount} species`} controlled`} +

+ )} +
+ +
{ + e.preventDefault(); + if (lookup.trim()) onViewAddress(lookup.trim()); + }} + > + setLookup(e.target.value)} + /> + +
+
+ + {error &&

{error}

} + {beasts === null && !error &&

Reading the collection…

} + {beasts?.length === 0 && ( +
+

{isYou ? 'You hold no Beasts yet.' : 'This address holds no Beasts.'}

+
+ )} + +
+ {sorted.map((beast) => ( + onOpenSpecies(beast.id)} + /> + ))} +
+
+ ); +} diff --git a/app/src/components/ConnectModal.tsx b/app/src/components/ConnectModal.tsx new file mode 100644 index 0000000..579ce2b --- /dev/null +++ b/app/src/components/ConnectModal.tsx @@ -0,0 +1,108 @@ +import { useConnect } from '@starknet-react/core'; +import type { Connector } from '@starknet-react/core'; +import { useState } from 'react'; + +interface Props { + onClose: () => void; +} + +/** + * Wallet picker. + * + * Connectors arrive from `WalletProvider`: Cartridge Controller first (it + * needs no extension, which is the likely case for a first-time artist), then + * any injected wallet. Recommended-but-not-installed wallets are still listed + * so the choice is visible rather than hidden — clicking one sends the artist + * to its download page, which is `InjectedConnector`'s own behaviour. + * + * Connection failures are shown here. Swallowing them is what makes a Connect + * button look broken. + */ +export function ConnectModal({ onClose }: Props) { + const { connect, connectors } = useConnect(); + const [pending, setPending] = useState(null); + const [error, setError] = useState(null); + + async function pick(connector: Connector) { + setPending(connector.id); + setError(null); + try { + await connect({ connector }); + onClose(); + } catch (e) { + setError(describe(e)); + } finally { + setPending(null); + } + } + + return ( +
+
e.stopPropagation()}> +
+

Connect a wallet

+ +
+ +

+ You need Sepolia STRK to register a Beast. Browsing and previewing + work without a wallet. +

+ +
    + {connectors.map((connector) => { + // Controller runs in a hosted iframe, so it is always available; + // injected wallets are only there if their extension is. + const installed = connector.available(); + return ( +
  • + +
  • + ); + })} +
+ + {connectors.some((c) => !c.available()) && ( +

+ Wallets marked “Not detected” need their browser extension + installed. Install it, then reload this page. +

+ )} + + {error &&

{error}

} +
+
+ ); +} + +function WalletIcon({ connector }: { connector: Connector }) { + const icon = connector.icon; + const src = typeof icon === 'string' ? icon : (icon?.dark ?? icon?.light); + if (!src) return ; + return ; +} + +function describe(error: unknown): string { + const message = error instanceof Error ? error.message : String(error); + if (/reject|denied|cancel/i.test(message)) return 'Connection cancelled.'; + if (/not found|no wallet|unavailable/i.test(message)) { + return 'That wallet is not installed in this browser.'; + } + return message; +} diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx new file mode 100644 index 0000000..72c23d2 --- /dev/null +++ b/app/src/components/Dashboard.tsx @@ -0,0 +1,335 @@ +import { + BEAST_TYPE_NAMES, + type BeastDefinition, + type BeastsClient, +} from '@provable-games/beasts-sdk'; +import { useEffect, useState } from 'react'; +import { ART_SLOTS, loadArtFile, type ArtSlot, type LoadedArt } from '../lib/art'; + +interface Props { + /** Connected wallet, needed as the `from` of a Genesis Beast transfer. */ + address?: string; + beastId: bigint; + definition: BeastDefinition; + client: BeastsClient; + isArtist: boolean; + onChanged: () => void; +} + +const ZERO = '0x0'; + +/** + * Per-species controls. Every action here is artist-only on-chain; the UI + * simply hides what the caller cannot do, and the contract is the real gate. + */ +export function Dashboard({ address, beastId, definition, client, isArtist, onChanged }: Props) { + const [busy, setBusy] = useState(null); + const [error, setError] = useState(null); + const [minter, setMinter] = useState(definition.minter); + const [statsSource, setStatsSource] = useState(definition.statsSource); + const [newArtist, setNewArtist] = useState(''); + const [customProvider, setCustomProvider] = useState(''); + const [art, setArt] = useState>>({}); + const [genesisTokenId, setGenesisTokenId] = useState(null); + + // The creator token's ID is derivable, but read it from the registry so the + // address shown is the one the contract will actually check. + useEffect(() => { + let cancelled = false; + void client + .getGenesisTokenId(beastId) + .then((id) => !cancelled && setGenesisTokenId(id)) + .catch(() => !cancelled && setGenesisTokenId(null)); + return () => { + cancelled = true; + }; + }, [client, beastId]); + + async function run(label: string, build: () => Parameters[0]) { + setBusy(label); + setError(null); + try { + await client.execute(build()); + onChanged(); + } catch (e) { + setError(e instanceof Error ? e.message : String(e)); + } finally { + setBusy(null); + } + } + + const paused = BigInt(definition.minter) === 0n; + const artComplete = ART_SLOTS.every(({ slot }) => art[slot]); + + return ( +
+
+
+

+ {definition.name} #{beastId.toString()} +

+

+ {BEAST_TYPE_NAMES[definition.beastType]} · Tier {definition.tier} ·{' '} + {definition.factoryProvider ? 'Verified art' : 'Custom provider'} +

+
+
+ {paused && Minting paused} + {definition.artLocked && Art locked} + {definition.minterLocked && Minter locked} +
+
+ + {!isArtist && ( +

+ You are not the artist for this species. These controls are read-only. +

+ )} + {error &&

{error}

} + +
+

Minting

+ {definition.minterLocked ? ( +

+ The minter is locked to {short(definition.minter)} and can + never change. Holders can rely on that. +

+ ) : ( + <> +

+ Set to zero to pause. Pausing stops new mints; it never affects + Beasts already minted. +

+
+ setMinter(e.target.value)} + /> + + +
+ + + )} +
+ +
+

Artwork

+ {definition.artLocked ? ( +

+ Art is locked.{' '} + {definition.factoryProvider + ? 'This species is frozen: the art is stored onchain and can never change.' + : 'The provider address is frozen, but a custom provider can still change what it returns — so refreshes stay available.'} +

+ ) : definition.factoryProvider ? ( + <> +

+ Replacing art re-renders every Beast of this species and notifies + marketplaces automatically. Limited to one refresh per hour. +

+
+ {ART_SLOTS.map(({ slot, label, kind, accept }) => ( + + ))} +
+ + + ) : ( + <> +

+ This species renders through your own provider at{' '} + {short(definition.artProvider)}. Announce changes so + marketplaces re-read it. +

+ + + )} + +
+ Advanced: swap art provider +

+ Point this species at your own IBeastArtProvider contract + to vary art by affix, tier or level. Your provider must return an + allowlisted image data URI or rendering will fail for your species. +

+
+ setCustomProvider(e.target.value)} + /> + +
+
+ + {!definition.artLocked && ( + + )} +
+ +
+

Kill stats

+

+ Optional. A stats source lets your Beasts show how many Adventurers + they have slain. It must implement the stats interface; set zero to + turn stats off. +

+
+ setStatsSource(e.target.value)} + /> + +
+
+ +
+

Transfer this species

+

+ Control of {definition.name} is ownership of its + Genesis Beast — there is no separate role. Sending the token hands the + new holder everything on this page, and you lose it. Selling the + Genesis Beast on any marketplace does the same thing. +

+

+ Genesis Beast #{beastId.toString()} · token{' '} + {genesisTokenId === null ? '…' : shortHex(genesisTokenId)} +

+
+ setNewArtist(e.target.value)} + /> + +
+
+
+ ); +} + +function shortHex(value: bigint): string { + const hex = value.toString(16); + return hex.length <= 12 ? `0x${hex}` : `0x${hex.slice(0, 6)}…${hex.slice(-4)}`; +} + +function short(address: string): string { + const hex = BigInt(address).toString(16); + if (hex === '0') return '0x0'; + return `0x${hex.slice(0, 4)}…${hex.slice(-4)}`; +} diff --git a/app/src/components/MySpecies.tsx b/app/src/components/MySpecies.tsx new file mode 100644 index 0000000..16f4af6 --- /dev/null +++ b/app/src/components/MySpecies.tsx @@ -0,0 +1,154 @@ +import { + BEAST_TYPE_NAMES, + type BeastsClient, + type OwnedSpecies, + decodeTokenId, +} from '@provable-games/beasts-sdk'; +import { useCallback, useEffect, useState } from 'react'; + +interface Props { + client: BeastsClient; + address: string; + onOpen: (beastId: bigint) => void; + onRegister: () => void; +} + +interface Row extends OwnedSpecies { + /** Undefined while loading; null when the provider could not be read. */ + art?: string | null; +} + +/** + * Everything the connected wallet controls. + * + * "Controls" means holding the artist role, which is what the registry's + * permissioned entrypoints actually check — not holding the Genesis Beast. + * The two start together but diverge the moment either is transferred, and + * showing the wrong one would offer controls that revert. + */ +export function MySpecies({ client, address, onOpen, onRegister }: Props) { + const [rows, setRows] = useState(null); + const [error, setError] = useState(null); + const [lookup, setLookup] = useState(''); + + const load = useCallback(async () => { + setRows(null); + setError(null); + try { + const owned = await client.getOwnedSpecies(address); + setRows(owned); + + // Art comes from each species' own provider, which for a custom + // provider is arbitrary contract code. One that reverts must cost its + // own thumbnail, not the whole list. + const withArt = await Promise.all( + owned.map(async (species) => { + try { + const beast = decodeTokenId(species.genesisTokenId); + return { ...species, art: await client.getArt(beast) }; + } catch { + return { ...species, art: null }; + } + }), + ); + setRows(withArt); + } catch (e) { + setError(e instanceof Error ? e.message : String(e)); + } + }, [client, address]); + + useEffect(() => { + void load(); + }, [load]); + + return ( +
+
+
+

Your Beasts

+

+ Species you hold the artist role for. That role — not the Genesis + Beast — is what the contract checks. +

+
+
{ + e.preventDefault(); + if (lookup) onOpen(BigInt(lookup)); + }} + > + setLookup(e.target.value.replace(/\D/g, ''))} + /> + +
+
+ + {error &&

{error}

} + + {rows === null && !error &&

Looking up your species…

} + + {rows?.length === 0 && ( +
+

You haven’t added a Beast yet.

+ +
+ )} + + {rows && rows.length > 0 && ( +
    + {rows.map((row) => { + const paused = BigInt(row.definition.minter) === 0n; + return ( +
  • + +
  • + ); + })} +
+ )} +
+ ); +} diff --git a/app/src/components/RegisterForm.tsx b/app/src/components/RegisterForm.tsx new file mode 100644 index 0000000..8393287 --- /dev/null +++ b/app/src/components/RegisterForm.tsx @@ -0,0 +1,243 @@ +import { useAccount } from '@starknet-react/core'; +import { + BEAST_TYPE_NAMES, + BeastType, + MAX_NAME_BYTES, + MAX_SUPPLY_PER_SPECIES, + validateSpeciesName, + validateTier, +} from '@provable-games/beasts-sdk'; +import { useMemo, useState } from 'react'; +import { ART_SLOTS, totalStorageSlots, type ArtSlot, type LoadedArt } from '../lib/art'; +import { ArtUpload } from './ArtUpload'; +import { CardPreview } from './CardPreview'; + +export interface RegistrationInput { + name: string; + tier: number; + beastType: BeastType; + minter: string; + art: { pngRegular: string; pngShiny: string; gifRegular: string; gifShiny: string }; +} + +interface Props { + onSubmit: (input: RegistrationInput) => void; + submitting: boolean; + error?: string; +} + +const ZERO = '0x0'; + +export function RegisterForm({ onSubmit, submitting, error }: Props) { + const { address } = useAccount(); + const [name, setName] = useState(''); + const [tier, setTier] = useState(3); + const [beastType, setBeastType] = useState(BeastType.Magic); + const [minterMode, setMinterMode] = useState<'later' | 'custom'>('later'); + const [minter, setMinter] = useState(''); + const [loaded, setLoaded] = useState>>({}); + const [preview, setPreview] = useState('pngRegular'); + + const nameCheck = name ? validateSpeciesName(name) : { valid: false }; + const tierCheck = validateTier(tier); + const missingArt = ART_SLOTS.filter(({ slot }) => !loaded[slot]); + const minterValid = minterMode === 'later' || /^0x[0-9a-fA-F]{1,64}$/.test(minter.trim()); + + const ready = + nameCheck.valid && tierCheck.valid && missingArt.length === 0 && minterValid && !!address; + + const slots = useMemo(() => totalStorageSlots(loaded), [loaded]); + + function submit() { + if (!ready) return; + onSubmit({ + name, + tier, + beastType, + minter: minterMode === 'later' ? ZERO : minter.trim(), + art: { + pngRegular: loaded.pngRegular!.dataUri, + pngShiny: loaded.pngShiny!.dataUri, + gifRegular: loaded.gifRegular!.dataUri, + gifShiny: loaded.gifShiny!.dataUri, + }, + }); + } + + return ( +
+
+
+

1. Artwork

+

+ All four variants are required. Which one a Beast shows is decided by + its token ID, so a species cannot ship a partial set. +

+ setLoaded((l) => ({ ...l, [slot]: art }))} + disabled={submitting} + /> + {slots > 0 && ( +

+ About {slots.toLocaleString()} storage slots. There is no size cap — + if you are willing to pay for it and the network accepts it, it is + valid — but larger art means a more expensive registration. +

+ )} +
+ +
+

2. Identity

+ + + + + + +
+ +
+

3. Who can mint it

+

+ Usually a Loot Survivor dungeon. Players earn your Beast by + defeating it there — Beasts are not sold, they are captured. +

+ + + + + + {minterMode === 'custom' && ( + + )} +
+ +
+

4. Register

+
    +
  • + Your wallet receives the Genesis Beast — the + creator's token, one per species, in the same transaction. +
  • +
  • + Your species is capped at {MAX_SUPPLY_PER_SPECIES.toLocaleString()}{' '} + Beasts forever. +
  • +
  • + Name, type and tier can never change. Art and + minter can, until you lock them. +
  • +
+ + {!address &&

Connect a wallet to register.

} + {missingArt.length > 0 && ( +

+ Still needed: {missingArt.map((s) => s.label).join(', ')} +

+ )} + {error &&

{error}

} + + +
+
+ + +
+ ); +} diff --git a/app/src/components/SpeciesCollection.tsx b/app/src/components/SpeciesCollection.tsx new file mode 100644 index 0000000..dd7e9ee --- /dev/null +++ b/app/src/components/SpeciesCollection.tsx @@ -0,0 +1,140 @@ +import { + BEAST_TYPE_NAMES, + MAX_SUPPLY_PER_SPECIES, + type Beast, + type BeastsClient, + type SpeciesSummary, + decodeTokenId, + isGenesis, +} from '@provable-games/beasts-sdk'; +import { useEffect, useState } from 'react'; +import { artFor, useArt } from '../lib/useArt'; +import { BeastTile } from './BeastTile'; + +interface Props { + client: BeastsClient; + beastId: bigint; + onBack: () => void; + onViewOwner: (address: string) => void; +} + +interface Entry { + beast: Beast; + rank: number; + owner?: string; +} + +/** + * Every Beast of one species, strongest first. + * + * Read from the contract's own per-species rank list rather than by scanning: + * the NFT already keeps `rank -> token_id` to drive metadata refreshes, so the + * ordering here is the same one the collection itself uses. + */ +export function SpeciesCollection({ client, beastId, onBack, onViewOwner }: Props) { + const [summary, setSummary] = useState(null); + const [entries, setEntries] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + setEntries([]); + setSummary(null); + setLoading(true); + setError(null); + + async function load() { + try { + const info = await client.getSpeciesSummary(beastId); + if (cancelled) return; + setSummary(info); + + const tokens = await client.getSpeciesTokens(beastId); + if (cancelled) return; + + const collected: Entry[] = []; + for (let i = 0; i < tokens.length && !cancelled; i += 4) { + const batch = await Promise.all( + tokens.slice(i, i + 4).map(async (tokenId) => { + const beast = decodeTokenId(tokenId); + const owner = await client.ownerOf(tokenId).catch(() => undefined); + return { beast, rank: isGenesis(beast) ? 0 : 0, owner }; + }), + ); + collected.push(...batch); + if (!cancelled) setEntries([...collected]); + } + + // Tokens arrive in rank order from the contract's list, with the + // Genesis Beast appended last. + if (!cancelled) { + setEntries( + collected.map((entry, index) => ({ + ...entry, + rank: isGenesis(entry.beast) ? 0 : index + 1, + })), + ); + } + } catch (e) { + if (!cancelled) setError(e instanceof Error ? e.message : String(e)); + } finally { + if (!cancelled) setLoading(false); + } + } + + void load(); + return () => { + cancelled = true; + }; + }, [client, beastId]); + + const art = useArt( + client, + entries.map((e) => e.beast), + ); + + return ( +
+ + +
+
+

+ {summary?.name ?? 'Species'} #{beastId.toString()} +

+ {summary && ( +

+ {BEAST_TYPE_NAMES[summary.beastType]} · Tier {summary.tier} ·{' '} + {summary.minted} of {MAX_SUPPLY_PER_SPECIES.toLocaleString()} minted + {summary.community ? '' : ' · one of the original 75'} +

+ )} +
+
+ + {error &&

{error}

} + {loading && entries.length === 0 && !error &&

Reading the species…

} + {!loading && entries.length === 0 && !error && ( +
+

No Beasts of this species have been minted yet.

+
+ )} + +
+ {entries.map((entry) => ( + onViewOwner(entry.owner!) : undefined} + /> + ))} +
+
+ ); +} diff --git a/app/src/components/SpeciesIndex.tsx b/app/src/components/SpeciesIndex.tsx new file mode 100644 index 0000000..0e4f430 --- /dev/null +++ b/app/src/components/SpeciesIndex.tsx @@ -0,0 +1,137 @@ +import { + BEAST_TYPE_NAMES, + type BeastsClient, + type SpeciesSummary, + genesisBeast, +} from '@provable-games/beasts-sdk'; +import { useEffect, useState } from 'react'; +import { artFor, useArt } from '../lib/useArt'; + +interface Props { + client: BeastsClient; + onOpen: (beastId: bigint) => void; +} + +/** + * The whole bestiary, one card per species. + * + * Grouping by species is the default because the collection is unbounded and + * a species can hold up to 1,243 Beasts — a flat list of every token would be + * unreadable long before it was useful. Selecting a species opens its own + * collection. + * + * Each card shows the species' Genesis Beast as the representative art: it is + * the one Beast guaranteed to exist for every registered species. + */ +export function SpeciesIndex({ client, onOpen }: Props) { + const [species, setSpecies] = useState([]); + const [total, setTotal] = useState(null); + const [error, setError] = useState(null); + const [showGenesis, setShowGenesis] = useState(false); + + useEffect(() => { + let cancelled = false; + setSpecies([]); + setError(null); + + async function load() { + try { + const count = Number(await client.speciesCount()); + if (cancelled) return; + setTotal(count); + + // Community species first — they are the ones that change. + const order = [ + ...range(76, count), + ...(showGenesis ? range(1, Math.min(75, count)) : []), + ]; + + const collected: SpeciesSummary[] = []; + for (let i = 0; i < order.length && !cancelled; i += 4) { + const batch = await Promise.all( + order.slice(i, i + 4).map((id) => + client.getSpeciesSummary(BigInt(id)).catch(() => null), + ), + ); + collected.push(...batch.filter((s): s is SpeciesSummary => s !== null)); + if (!cancelled) setSpecies([...collected]); + } + } catch (e) { + if (!cancelled) setError(e instanceof Error ? e.message : String(e)); + } + } + + void load(); + return () => { + cancelled = true; + }; + }, [client, showGenesis]); + + const representatives = species.map((s) => genesisBeast(s.beastId, s.tier, s.beastType)); + const art = useArt(client, representatives); + + return ( +
+
+
+

The bestiary

+

+ {total === null + ? 'Counting species…' + : showGenesis + ? `All ${total} species. Open one to see every Beast of its kind.` + : `${Math.max(0, total - 75)} community ${total - 75 === 1 ? 'species' : 'species'} of ${total}. Open one to see every Beast of its kind.`} +

+
+ +
+ + {error &&

{error}

} + {species.length === 0 && !error &&

Loading species…

} + +
+ {species.map((s, index) => ( + + ))} +
+
+ ); +} + +function range(from: number, to: number): number[] { + return to < from ? [] : Array.from({ length: to - from + 1 }, (_, i) => from + i); +} diff --git a/app/src/lib/WalletProvider.tsx b/app/src/lib/WalletProvider.tsx new file mode 100644 index 0000000..da3a732 --- /dev/null +++ b/app/src/lib/WalletProvider.tsx @@ -0,0 +1,47 @@ +import { + StarknetConfig, + braavos, + jsonRpcProvider, + ready, + useInjectedConnectors, + voyager, +} from '@starknet-react/core'; +import type { PropsWithChildren } from 'react'; +import { RPC_URL, chains, controllerConnector } from './chain'; + +/** + * Wallet wiring for the app. + * + * Two kinds of wallet, deliberately both supported: Cartridge Controller + * (no extension, good for a first-time artist) and any injected browser + * wallet — Ready (formerly Argent) and Braavos are surfaced as recommended + * even when not installed, so the picker shows what is possible rather than + * only what is present. + * + * `useInjectedConnectors` scans `window.starknet*`, so it must run inside a + * component; the connector list is then handed to `StarknetConfig` as a prop. + */ +export function WalletProvider({ children }: PropsWithChildren) { + const { connectors: injected } = useInjectedConnectors({ + recommended: [ready(), braavos()], + includeRecommended: 'always', + order: 'alphabetical', + }); + + // `publicProvider()` pins RPC spec 0.8.1, which starknet 9.x dropped — it + // throws before the tree ever renders. Naming the node lets the provider + // negotiate whatever that node actually speaks. + const rpc = () => ({ nodeUrl: RPC_URL }); + + return ( + + {children} + + ); +} diff --git a/app/src/lib/art.ts b/app/src/lib/art.ts new file mode 100644 index 0000000..13a016e --- /dev/null +++ b/app/src/lib/art.ts @@ -0,0 +1,109 @@ +import { validateFactoryArt, type ValidationResult } from '@provable-games/beasts-sdk'; + +export type ArtSlot = 'pngRegular' | 'pngShiny' | 'gifRegular' | 'gifShiny'; + +export interface ArtSlotSpec { + slot: ArtSlot; + label: string; + hint: string; + kind: 'png' | 'gif'; + accept: string; +} + +/** + * The four variants a factory-provider species stores. Which one a token + * renders is decided by the shiny and animated bits in its token ID, so all + * four are required — a species cannot ship a partial set. + */ +export const ART_SLOTS: readonly ArtSlotSpec[] = [ + { + slot: 'pngRegular', + label: 'Standard', + hint: 'The default look. Shown when a Beast is neither shiny nor animated.', + kind: 'png', + accept: 'image/png', + }, + { + slot: 'pngShiny', + label: 'Shiny', + hint: 'The rare colourway. Static.', + kind: 'png', + accept: 'image/png', + }, + { + slot: 'gifRegular', + label: 'Animated', + hint: 'The default look, in motion.', + kind: 'gif', + accept: 'image/gif', + }, + { + slot: 'gifShiny', + label: 'Animated + Shiny', + hint: 'The rarest combination. Both bits set.', + kind: 'gif', + accept: 'image/gif', + }, +]; + +export interface LoadedArt { + dataUri: string; + bytes: number; + /** On-chain storage cost: art is stored 31 bytes per felt slot. */ + slots: number; +} + +export class ArtLoadError extends Error {} + +/** + * Reads a file into a base64 data URI and checks it against the same rules + * the factory provider enforces on write. + * + * The browser's own decode is the real format check — magic bytes only prove + * the header, and a file that cannot be decoded here would render as a broken + * image for every holder of the species. + */ +export async function loadArtFile(file: File, kind: 'png' | 'gif'): Promise { + const dataUri = await readAsDataUri(file); + + const validation: ValidationResult = validateFactoryArt(dataUri, kind); + if (!validation.valid) throw new ArtLoadError(validation.error ?? 'Invalid art'); + + await assertDecodable(dataUri); + + const bytes = dataUri.length; + return { dataUri, bytes, slots: Math.ceil(bytes / 31) }; +} + +function readAsDataUri(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = () => reject(new ArtLoadError('Could not read that file')); + reader.onload = () => { + const result = reader.result; + if (typeof result !== 'string') { + reject(new ArtLoadError('Could not read that file')); + return; + } + resolve(result); + }; + reader.readAsDataURL(file); + }); +} + +function assertDecodable(dataUri: string): Promise { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => resolve(); + image.onerror = () => reject(new ArtLoadError('That file is not a valid image')); + image.src = dataUri; + }); +} + +/** + * Rough cost signal for the registration transaction. Advisory only — the + * contract imposes no size cap, and the network decides what it will accept. + */ +export function totalStorageSlots(loaded: Partial>): number { + return ART_SLOTS.reduce((sum, { slot }) => sum + (loaded[slot]?.slots ?? 0), 0); +} diff --git a/app/src/lib/chain.ts b/app/src/lib/chain.ts new file mode 100644 index 0000000..2f00f34 --- /dev/null +++ b/app/src/lib/chain.ts @@ -0,0 +1,49 @@ +import ControllerConnector from '@cartridge/connector/controller'; +import { sepolia } from '@starknet-react/chains'; +import { SEPOLIA_ADDRESSES } from '@provable-games/beasts-sdk'; +import { RpcProvider } from 'starknet'; + +export const RPC_URL = + import.meta.env.VITE_STARKNET_RPC_URL ?? + 'https://api.cartridge.gg/x/starknet/sepolia'; + +export const ADDRESSES = { + nft: import.meta.env.VITE_BEASTS_NFT ?? SEPOLIA_ADDRESSES.nft, + registry: import.meta.env.VITE_BEASTS_REGISTRY ?? SEPOLIA_ADDRESSES.registry, +}; + +/** Sepolia only. Everything this app talks to is deployed there. */ +export const chains = [sepolia]; + +export const provider = () => new RpcProvider({ nodeUrl: RPC_URL }); + +/** + * Cartridge Controller with the registry's *management* entrypoints + * pre-approved, so an artist signs once and can then iterate on their species — + * update art, rotate the minter, refresh metadata — without a popup for every + * change. + * + * `register_beast_with_art` is deliberately NOT in the session policy: it + * mints the artist's provenance token and permanently assigns a species ID, so + * it should always be an explicit, visible signature. + */ +export const controllerConnector = new ControllerConnector({ + chains: [{ rpcUrl: RPC_URL }], + // Must be 0x-prefixed hex; the bare digits parse as neither hex nor decimal. + defaultChainId: `0x${sepolia.id.toString(16)}`, + propagateSessionErrors: true, + policies: { + contracts: { + [ADDRESSES.registry]: { + name: 'Beast Registry', + methods: [ + { name: 'Update art', entrypoint: 'update_art' }, + { name: 'Set minter', entrypoint: 'set_minter' }, + { name: 'Set art provider', entrypoint: 'set_art_provider' }, + { name: 'Refresh metadata', entrypoint: 'notify_art_updated' }, + { name: 'Set stats source', entrypoint: 'set_stats_source' }, + ], + }, + }, + }, +}); diff --git a/app/src/lib/router.ts b/app/src/lib/router.ts new file mode 100644 index 0000000..60d2372 --- /dev/null +++ b/app/src/lib/router.ts @@ -0,0 +1,72 @@ +import { useCallback, useEffect, useState } from 'react'; + +/** + * Hash routing, hand-rolled. + * + * A hash keeps every page a real, shareable URL without needing server + * rewrites — this app is a static bundle. The address lives in the path + * rather than in component state precisely so `#/collection/0xabc…` can be + * edited to any wallet and shared as-is. + */ +export type Route = + | { name: 'register' } + | { name: 'collection'; address: string } + | { name: 'species-index' } + | { name: 'species'; beastId: bigint } + | { name: 'manage' } + | { name: 'manage-species'; beastId: bigint }; + +export function parseRoute(hash: string): Route { + const path = hash.replace(/^#\/?/, '').split('?')[0]; + const [head, tail] = path.split('/'); + + switch (head) { + case 'collection': + return tail ? { name: 'collection', address: tail } : { name: 'register' }; + case 'beasts': + if (!tail) return { name: 'species-index' }; + return isDigits(tail) ? { name: 'species', beastId: BigInt(tail) } : { name: 'species-index' }; + case 'manage': + if (!tail) return { name: 'manage' }; + return isDigits(tail) ? { name: 'manage-species', beastId: BigInt(tail) } : { name: 'manage' }; + default: + return { name: 'register' }; + } +} + +export function href(route: Route): string { + switch (route.name) { + case 'collection': + return `#/collection/${route.address}`; + case 'species-index': + return '#/beasts'; + case 'species': + return `#/beasts/${route.beastId}`; + case 'manage': + return '#/manage'; + case 'manage-species': + return `#/manage/${route.beastId}`; + default: + return '#/'; + } +} + +export function useRoute(): [Route, (route: Route) => void] { + const [hash, setHash] = useState(() => window.location.hash); + + useEffect(() => { + const onChange = () => setHash(window.location.hash); + window.addEventListener('hashchange', onChange); + return () => window.removeEventListener('hashchange', onChange); + }, []); + + const navigate = useCallback((route: Route) => { + window.location.hash = href(route); + }, []); + + return [parseRoute(hash), navigate]; +} + +function isDigits(value: string | undefined): value is string { + return !!value && /^\d+$/.test(value); +} diff --git a/app/src/lib/useArt.ts b/app/src/lib/useArt.ts new file mode 100644 index 0000000..7d4d016 --- /dev/null +++ b/app/src/lib/useArt.ts @@ -0,0 +1,58 @@ +import { type Beast, type BeastsClient, encodeTokenId } from '@provable-games/beasts-sdk'; +import { useEffect, useState } from 'react'; + +/** + * Art is fetched per Beast, not per species. + * + * A community provider receives the whole decoded Beast, so it may legitimately + * vary art by affix, tier or level — caching by species would show the wrong + * picture for exactly the providers that make the interface worth having. + * Keyed by token ID and shared across pages, so navigating back is free. + */ +const cache = new Map(); + +/** Fetches art for a list of Beasts, filling in progressively. */ +export function useArt( + client: BeastsClient, + beasts: Beast[], +): Map { + const [, setTick] = useState(0); + const key = beasts.map((b) => encodeTokenId(b).toString()).join(','); + + useEffect(() => { + let cancelled = false; + + async function load() { + // Bounded batches: one request per Beast will out-run a public node's + // per-second budget on any collection worth showing. + const pending = beasts.filter((b) => !cache.has(encodeTokenId(b).toString())); + for (let i = 0; i < pending.length && !cancelled; i += 4) { + await Promise.all( + pending.slice(i, i + 4).map(async (beast) => { + const id = encodeTokenId(beast).toString(); + try { + cache.set(id, await client.getArt(beast)); + } catch { + // A provider that reverts costs its own thumbnail, nothing more. + cache.set(id, null); + } + }), + ); + if (!cancelled) setTick((t) => t + 1); + } + } + + void load(); + return () => { + cancelled = true; + }; + // `key` collapses the beast list to a stable identity. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [client, key]); + + return cache; +} + +export function artFor(cache: Map, beast: Beast): string | null | undefined { + return cache.get(encodeTokenId(beast).toString()); +} diff --git a/app/src/main.tsx b/app/src/main.tsx new file mode 100644 index 0000000..7aac99c --- /dev/null +++ b/app/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { App } from './App'; +import { WalletProvider } from './lib/WalletProvider'; +import './styles.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + , +); diff --git a/app/src/styles.css b/app/src/styles.css new file mode 100644 index 0000000..321cf8b --- /dev/null +++ b/app/src/styles.css @@ -0,0 +1,929 @@ +:root { + --bg: #131316; + --panel: #1e1e22; + --panel-2: #2d2d32; + --line: #ffffff14; + --text: #ececf0; + --muted: #9a9aa4; + --gold: #b79a5e; + --gold-bright: #e5d8b2; + --danger: #d4674f; + --radius: 10px; + color-scheme: dark; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); + font: 15px/1.55 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif; +} + +code { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 0.9em; + color: var(--gold-bright); +} + +.app { + max-width: 1140px; + margin: 0 auto; + padding: 0 20px 64px; +} + +/* ------------------------------------------------------------- topbar */ + +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 18px 0; + border-bottom: 1px solid var(--line); + flex-wrap: wrap; +} + +.brand { + background: none; + border: 0; + padding: 0; + color: var(--gold-bright); + font-size: 17px; + font-weight: 600; + letter-spacing: 0.01em; + cursor: pointer; +} + +.topbar__right { + display: flex; + align-items: center; + gap: 10px; +} + +.lookup { + display: flex; + gap: 6px; +} + +.lookup input { + width: 110px; +} + +/* The collection page's lookup takes a whole address, not an ID. */ +.page__header .lookup input { + width: 240px; +} + +/* ------------------------------------------------------------ controls */ + +button { + background: var(--panel-2); + color: var(--text); + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 9px 14px; + font: inherit; + font-size: 14px; + cursor: pointer; + transition: background 0.15s, border-color 0.15s; +} + +button:hover:not(:disabled) { + border-color: #ffffff2e; + background: #35353c; +} + +button:disabled { + opacity: 0.45; + cursor: not-allowed; +} + +button.primary { + background: linear-gradient(135deg, var(--gold-bright), var(--gold)); + color: #23201a; + border-color: transparent; + font-weight: 600; +} + +button.danger { + border-color: #d4674f4d; + color: var(--danger); + background: transparent; + margin-top: 12px; +} + +button.danger:hover:not(:disabled) { + background: #d4674f1a; +} + +input, +select { + background: #16161a; + color: var(--text); + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 9px 11px; + font: inherit; + font-size: 14px; + width: 100%; +} + +input:focus, +select:focus { + outline: none; + border-color: var(--gold); +} + +/* --------------------------------------------------------------- hero */ + +.hero { + padding: 44px 0 28px; + max-width: 640px; +} + +.hero h1 { + margin: 0 0 12px; + font-size: 34px; + line-height: 1.15; + letter-spacing: -0.02em; +} + +.hero p { + margin: 0 0 8px; + color: #c9c9d2; +} + +.muted { + color: var(--muted); + font-size: 14px; +} + +/* ----------------------------------------------------------- register */ + +.register { + display: grid; + grid-template-columns: minmax(0, 1fr) 300px; + gap: 36px; + align-items: start; +} + +@media (max-width: 900px) { + .register { + grid-template-columns: 1fr; + } +} + +.register__form section { + padding: 22px 0; + border-top: 1px solid var(--line); +} + +.register__form h2 { + margin: 0 0 6px; + font-size: 16px; + letter-spacing: 0.01em; +} + +.register__preview { + position: sticky; + top: 20px; +} + +.field { + display: block; + margin: 14px 0; +} + +.field > span { + display: block; + margin-bottom: 6px; + font-size: 13px; + color: var(--muted); +} + +.field__hint, +.field__error { + display: block; + margin-top: 6px; + font-size: 12.5px; + font-style: normal; +} + +.field__hint { + color: var(--muted); +} + +.field__error { + color: var(--danger); +} + +.radio { + display: flex; + gap: 10px; + align-items: flex-start; + padding: 11px 0; + cursor: pointer; +} + +.radio input { + width: auto; + margin-top: 3px; +} + +.radio span { + display: block; +} + +.radio em { + display: block; + color: var(--muted); + font-size: 13px; + font-style: normal; + margin-top: 2px; +} + +.summary { + margin: 0 0 18px; + padding-left: 18px; + color: #c9c9d2; + font-size: 14px; +} + +.summary li { + margin: 6px 0; +} + +/* ------------------------------------------------------------ art grid */ + +.art-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 12px; + margin: 16px 0; +} + +.art-slot { + background: var(--panel); + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 12px; +} + +.art-slot--selected { + border-color: var(--gold); +} + +.art-slot--error { + border-color: var(--danger); +} + +.art-slot__header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 8px; +} + +.art-slot__label { + font-size: 13px; + font-weight: 600; +} + +.art-slot__kind { + font-size: 11px; + color: var(--muted); +} + +.art-slot__drop { + display: block; + position: relative; + aspect-ratio: 1; + background: #000; + border-radius: 6px; + overflow: hidden; + cursor: pointer; + display: grid; + place-items: center; +} + +.art-slot__drop input, +.art-slot--compact input { + position: absolute; + inset: 0; + opacity: 0; + cursor: pointer; + width: 100%; +} + +.art-slot img { + width: 100%; + height: 100%; + object-fit: contain; + image-rendering: pixelated; +} + +.art-slot__placeholder { + color: var(--muted); + font-size: 12px; +} + +.art-slot__hint { + margin: 8px 0 0; + font-size: 12px; + color: var(--muted); +} + +.art-slot__meta { + margin: 4px 0 0; + font-size: 11.5px; + color: var(--gold); +} + +.art-slot__error { + margin: 6px 0 0; + font-size: 12px; + color: var(--danger); +} + +.art-grid--compact { + grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); +} + +.art-slot--compact { + position: relative; + display: grid; + gap: 6px; + place-items: center; + cursor: pointer; +} + +.art-slot--compact img { + aspect-ratio: 1; +} + +/* ---------------------------------------------------------------- card */ + +.card { + background: linear-gradient(180deg, var(--panel-2), var(--panel)); + border: 2px solid transparent; + border-radius: 14px; + padding: 14px; + background-clip: padding-box; + box-shadow: 0 0 0 2px #ffffff10; +} + +.card--shiny { + box-shadow: 0 0 0 2px var(--gold), 0 0 22px #b79a5e40; +} + +.card__title { + font-size: 14px; + font-weight: 600; + color: var(--gold-bright); + text-align: center; + margin-bottom: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.card__art { + position: relative; + aspect-ratio: 220 / 144; + background: #000; + border-radius: 8px; + display: grid; + place-items: center; + overflow: hidden; +} + +/* Absolute inset is what gives the image a definite box to fit inside. + A percentage height against an `aspect-ratio` parent resolves to `auto`, + so a square Beast would size itself from its own 1:1 ratio, overflow the + wider frame, and get cropped top and bottom by `overflow: hidden`. */ +.card__art img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + image-rendering: pixelated; +} + +.card__art-empty { + color: var(--muted); + font-size: 12px; +} + +.card__stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + margin-top: 10px; +} + +.stat { + background: var(--panel); + border-radius: 6px; + padding: 7px 8px; + text-align: center; +} + +.stat__label { + font-size: 10px; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.stat__value { + font-size: 15px; + font-weight: 600; + color: var(--gold-bright); +} + +.card__note { + margin: 12px 0 0; + font-size: 11.5px; + color: var(--muted); + text-align: center; +} + +/* ----------------------------------------------------------- dashboard */ + +.dashboard { + padding-top: 32px; +} + +.dashboard__header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; + flex-wrap: wrap; + padding-bottom: 18px; + border-bottom: 1px solid var(--line); +} + +.dashboard__header h2 { + margin: 0; + font-size: 24px; +} + +.dashboard__header p { + margin: 4px 0 0; +} + +.badges { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.badge { + border: 1px solid var(--line); + border-radius: 999px; + padding: 4px 11px; + font-size: 12px; + color: var(--muted); +} + +.badge--warn { + border-color: #d4674f4d; + color: var(--danger); +} + +.panel { + padding: 22px 0; + border-bottom: 1px solid var(--line); +} + +.panel h3 { + margin: 0 0 6px; + font-size: 15px; +} + +.panel p { + margin: 0 0 12px; +} + +.row { + display: flex; + gap: 8px; + margin: 12px 0; + flex-wrap: wrap; +} + +.row input { + flex: 1 1 240px; +} + +.row button { + white-space: nowrap; +} + +.advanced { + margin-top: 16px; + border-top: 1px solid var(--line); + padding-top: 14px; +} + +.advanced summary { + cursor: pointer; + font-size: 14px; + color: var(--muted); +} + +.notice { + background: #b79a5e1a; + border: 1px solid #b79a5e33; + border-radius: var(--radius); + padding: 11px 14px; + font-size: 14px; + margin: 18px 0; +} + +.empty { + padding: 80px 0; + text-align: center; + display: grid; + gap: 14px; + place-items: center; +} + +.footer { + margin-top: 48px; + padding-top: 18px; + border-top: 1px solid var(--line); + font-size: 12.5px; + color: var(--muted); +} + +.modal__hint { + margin: 14px 0 0; + font-size: 12.5px; +} + +/* ---------------------------------------------------------- connect modal */ + +.modal-backdrop { + position: fixed; + inset: 0; + background: #000000b8; + display: grid; + place-items: center; + padding: 20px; + z-index: 50; +} + +.modal { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 14px; + padding: 22px; + width: 100%; + max-width: 380px; + box-shadow: 0 20px 60px #00000080; +} + +.modal__header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; +} + +.modal__header h3 { + margin: 0; + font-size: 17px; +} + +.modal__close { + background: none; + border: 0; + color: var(--muted); + font-size: 24px; + line-height: 1; + padding: 0 4px; + cursor: pointer; +} + +.wallets { + list-style: none; + margin: 18px 0 0; + padding: 0; + display: grid; + gap: 8px; +} + +.wallet { + display: flex; + align-items: center; + gap: 11px; + width: 100%; + text-align: left; + padding: 12px 14px; +} + +.wallet__icon { + width: 24px; + height: 24px; + border-radius: 6px; + object-fit: contain; + flex: none; +} + +.wallet__icon--blank { + background: var(--panel-2); +} + +.wallet__name { + flex: 1; + font-weight: 500; +} + +.wallet__state { + font-size: 12px; + color: var(--muted); +} + +/* ------------------------------------------------------------ my species */ + +.mine { + padding-top: 36px; +} + +.mine__header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 20px; + flex-wrap: wrap; + padding-bottom: 20px; + border-bottom: 1px solid var(--line); +} + +.mine__header h1 { + margin: 0 0 6px; + font-size: 26px; + letter-spacing: -0.01em; +} + +.mine__header p { + margin: 0; + max-width: 46ch; +} + +.species-grid { + list-style: none; + margin: 22px 0 0; + padding: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 14px; +} + +.species-card { + display: flex; + gap: 14px; + width: 100%; + text-align: left; + align-items: center; + padding: 14px; + background: var(--panel); +} + +.species-card__art { + position: relative; + width: 72px; + height: 72px; + flex: none; + background: #000; + border-radius: 8px; + overflow: hidden; + display: grid; + place-items: center; +} + +.species-card__art img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + image-rendering: pixelated; +} + +.species-card__art-empty { + font-size: 10.5px; + color: var(--muted); + text-align: center; + padding: 0 6px; +} + +.species-card__body { + min-width: 0; +} + +.species-card__title { + font-weight: 600; + margin-bottom: 3px; +} + +.species-card .badges { + margin-top: 8px; +} + +.badge--ok { + border-color: #6f9e6a4d; + color: #8fbe89; +} + +.backlink { + background: none; + border: 0; + padding: 0; + margin: 26px 0 0; + color: var(--muted); + font-size: 14px; + cursor: pointer; +} + +.backlink:hover { + color: var(--text); + background: none; +} + +/* ---------------------------------------------------------------- pages */ + +.page { + padding-top: 32px; +} + +.page__header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 20px; + flex-wrap: wrap; + padding-bottom: 20px; + border-bottom: 1px solid var(--line); +} + +.page__header h1 { + margin: 0 0 6px; + font-size: 26px; + letter-spacing: -0.01em; +} + +.page__header p { + margin: 0 0 4px; + max-width: 60ch; + word-break: break-all; +} + +.navlink { + color: var(--text); + text-decoration: none; + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 9px 14px; + font-size: 14px; + background: var(--panel-2); + transition: background 0.15s, border-color 0.15s; +} + +.navlink:hover { + border-color: #ffffff2e; + background: #35353c; +} + +a.brand { + text-decoration: none; +} + +.toggle { + display: flex; + align-items: center; + gap: 8px; + font-size: 13.5px; + color: var(--muted); + cursor: pointer; + white-space: nowrap; +} + +.toggle input { + width: auto; +} + +/* ---------------------------------------------------------------- tiles */ + +.tiles { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + gap: 14px; + margin-top: 22px; +} + +.tile { + background: linear-gradient(180deg, var(--panel-2), var(--panel)); + border: 1px solid var(--line); + border-radius: 12px; + padding: 12px; + text-align: left; + width: 100%; + display: block; +} + +.tile--clickable { + cursor: pointer; +} + +.tile--clickable:hover { + border-color: var(--gold); + background: linear-gradient(180deg, #35353c, var(--panel)); +} + +.tile__art { + position: relative; + aspect-ratio: 1; + background: #000; + border-radius: 8px; + overflow: hidden; + display: grid; + place-items: center; + margin-bottom: 10px; +} + +.tile__art--shiny { + box-shadow: inset 0 0 0 2px var(--gold); +} + +/* Absolute inset gives the image a definite box; a percentage height against + an aspect-ratio parent resolves to auto and a square Beast would overflow. */ +.tile__art img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + image-rendering: pixelated; +} + +.tile__art-note { + font-size: 11px; + color: var(--muted); + text-align: center; + padding: 0 8px; +} + +.tile__badge { + position: absolute; + top: 6px; + left: 6px; + background: #000000b0; + border: 1px solid var(--line); + border-radius: 999px; + padding: 2px 8px; + font-size: 10px; + color: var(--gold-bright); + letter-spacing: 0.03em; +} + +.tile__badge--corner { + left: auto; + right: 6px; + color: var(--muted); +} + +.tile__name { + font-size: 13.5px; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.tile__meta { + font-size: 12px; + color: var(--muted); + margin-top: 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.tile__stats { + display: flex; + justify-content: space-between; + gap: 6px; + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid var(--line); + font-size: 11.5px; + color: var(--muted); +} diff --git a/app/src/vite-env.d.ts b/app/src/vite-env.d.ts new file mode 100644 index 0000000..8e73e6d --- /dev/null +++ b/app/src/vite-env.d.ts @@ -0,0 +1,11 @@ +/// + +interface ImportMetaEnv { + readonly VITE_STARKNET_RPC_URL?: string; + readonly VITE_BEASTS_NFT?: string; + readonly VITE_BEASTS_REGISTRY?: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/app/tsconfig.json b/app/tsconfig.json new file mode 100644 index 0000000..c9d766b --- /dev/null +++ b/app/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "noEmit": true, + "allowImportingTsExtensions": true, + "isolatedModules": true + }, + "include": ["src"] +} diff --git a/app/vite.config.ts b/app/vite.config.ts new file mode 100644 index 0000000..fabde1a --- /dev/null +++ b/app/vite.config.ts @@ -0,0 +1,6 @@ +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [react()], +}); diff --git a/docs/sepolia-v3-deployment.md b/docs/sepolia-v3-deployment.md index e26fc0e..b501a89 100644 --- a/docs/sepolia-v3-deployment.md +++ b/docs/sepolia-v3-deployment.md @@ -1,7 +1,8 @@ # Beasts V3 — Sepolia deployment -Deployed 2026-07-31 from branch `feat/nft-registry-integration` (PR #20, -stacked on #19). Tooling: **sncast 0.60.0** (`--network sepolia`). Not +Redeployed 2026-07-31 from `feat/nft-registry-integration` (PR #20) after +owner enumeration merged (#14) and the artist role became Genesis Beast +ownership. Supersedes the earlier deployment; those addresses are dead. Tooling: **sncast 0.60.0** (`--network sepolia`). Not starkli — it is unsupported and absent from this environment. Deployer / owner / royalty receiver: sncast account `commit-reveal-sepolia` @@ -13,8 +14,8 @@ Total cost: ~10 STRK. | Contract | Address | |---|---| -| **beasts_nft** | `0x01dac77837c6751777d917051a6e405967c5c75f46df5ab7c635e52819634bfd` | -| **beast_registry** | `0x06d46c98087a1246182c6cd8ef144ee0a67da6e6cc9e44e39aef08cf92d30045` | +| **beasts_nft** | `0x017e2cb5d7c4a86ff2bdee182ce53386a7cc57c63b943878de21b681e336a89a` | +| **beast_registry** | `0x0797a19c0b267e91ea17f886f155310e38196261ec5683e3a12a35772718d723` | | beast_png_regular_data | `0x045f6cf8249ebee56f699a46cb66f02cbd23419f1c2cd3e62a3dfdedaf894279` | | beast_png_shiny_data | `0x0291ad81a428262fd709f0075dedf69814173bf5b60f989cf7095f5efa72c670` | | beast_gif_regular_data | `0x04a15db02fc7c991f2080e349cfbfb8f96f5fd61dd92c1cbe60ed7dbd4d49bfe` | @@ -24,8 +25,8 @@ Total cost: ~10 STRK. | Contract | Class hash | |---|---| -| beasts_nft | `0x350e97a3244fecad9f850d84843a0effc26a364c392c2e8c4379cb5de0193ea` | -| beast_registry | `0x2afeefe9818b1c3fa839cef077cad5c6767bda41e6737e31ed44ed1a3fd6a97` | +| beasts_nft | `0x20f43e08d7d6e8802809b12af5692bf596ef929079607eed48de71afceee8ff` | +| beast_registry | `0x24809e1053dae99db897c32bb67fe323ac74d1e1fc47a26ddf7362a3f883041` | | stored_art_provider | `0x2e3011cf968bbea8b72e75efdfe120318ccf61fe711d2f7f927114e2d8da56e` | | beast_png_regular_data | `0x15d5742d2e7804531ac456b7ba82e9dc961ba154cbaaad631e2e7b4e887b68b` | | beast_png_shiny_data | `0x3a1bfcae2737a12df248675d57c3a1a94eeceb5a696f14fa6f23fd99bf3d247` | @@ -41,10 +42,15 @@ Both pointers are write-once and both are required; a stack missing either accepts no community species at all. ``` -registry.set_nft_address(0x01dac778...) tx 0x06a0634be77503b1e32507406b046d320098f2f17a0142cc56dba07639e22fd5 -nft.set_registry_address(0x06d46c98...) tx 0x02af8798debaea3fbd5949bafe2b506e048a8fed8cde08101e63149fa4236ded +registry.set_nft_address(0x017e2cb5...) tx 0x058ba4ac366347b3271fa061219d17c29efe9b4dbe98666a1906a9a87cf059c7 +nft.set_registry_address(0x0797a19c...) tx (same session) ``` +The four art data contracts and the `stored_art_provider` class are unchanged +from the first deployment and were reused — `stored_art_provider` recompiled +to a byte-identical class hash, which the network confirmed by rejecting the +redeclare. + `dungeon_address` is **unset (zero)**, so genesis species 1–75 cannot be minted yet. That is deliberate: it is also the state mainnet must launch in until the `burn_and_mint` migration completes, or a dungeon could claim an @@ -58,10 +64,14 @@ until the `burn_and_mint` migration completes, or a dungeon could claim an | Genesis token ownership | `owner_of(0x7006400010000000000000000001)` → deployer | | Genesis render (legacy art path) | `token_uri` → `"Warlock"`, new bestiary description | | Permissionless registration | `register_beast_with_art('Gloomfang', Hunter, 3, ...)` → species **76** | -| Factory art provider auto-deploy | `0x4b11caad7b2b29949f957c0854d2fa3a37fc40519bfee2eb844d8f319ac19b9`, `factory_provider: true` | +| Factory art provider auto-deploy | `0x0bdc56ee6a9fef516d311188bc563c43f3d49e2af640b9d4c5b11fb134ad95d`, `factory_provider: true` | | Provenance mint | `total_supply()` 75 → 76 | | Per-species mint auth | `mint(..., 76, 1, 1, 10, 100, 0, 1)` from the registered minter succeeded | | Community render | `"Agony Bane" Gloomfang`, Rank 1, 20,970-byte SVG | +| Owner enumeration | `token_of_owner_by_index(owner, 0)` → `0x7006400010000000000000000001`, the genesis Warlock | +| Artist = Genesis Beast | `get_artist(76)` returns the holder of `get_genesis_token_id(76)` | +| Role follows the token | transferring the creator token moved `get_artist(76)` to the new holder | +| Seller loses control | the previous holder's `set_minter` then reverted `Registry: not artist`, while the new holder's succeeded | Decoded attributes of the community mint — every value routed correctly: diff --git a/package.json b/package.json new file mode 100644 index 0000000..b8aedb0 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "beasts-monorepo", + "private": true, + "scripts": { + "test:sdk": "pnpm --filter @provable-games/beasts-sdk test", + "build:sdk": "pnpm --filter @provable-games/beasts-sdk build", + "dev:app": "pnpm --filter @provable-games/beasts-app dev", + "build:app": "pnpm --filter @provable-games/beasts-app build", + "typecheck": "pnpm -r typecheck", + "gen:tables": "node scripts/gen-tables.mjs" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..29e6fa3 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5924 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + app: + dependencies: + '@cartridge/connector': + specifier: ^0.13.12 + version: 0.13.16(@starknet-react/core@5.0.3(get-starknet-core@4.0.0)(react@18.3.1)(starknet@9.4.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@cartridge/controller': + specifier: ^0.13.12 + version: 0.13.16(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@provable-games/beasts-sdk': + specifier: workspace:* + version: link:../sdk + '@starknet-react/chains': + specifier: 5.0.3 + version: 5.0.3 + '@starknet-react/core': + specifier: ^5.0.1 + version: 5.0.3(get-starknet-core@4.0.0)(react@18.3.1)(starknet@9.4.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3) + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + starknet: + specifier: 9.4.2 + version: 9.4.2(typescript@5.9.3)(zod@3.25.76) + devDependencies: + '@types/react': + specifier: ^18.2.0 + version: 18.3.31 + '@types/react-dom': + specifier: ^18.2.0 + version: 18.3.7(@types/react@18.3.31) + '@vitejs/plugin-react': + specifier: ^4.3.0 + version: 4.7.0(vite@5.4.21(@types/node@22.7.5)) + playwright: + specifier: ^1.62.1 + version: 1.62.1 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + vite: + specifier: ^5.4.0 + version: 5.4.21(@types/node@22.7.5) + + sdk: + devDependencies: + starknet: + specifier: 9.4.2 + version: 9.4.2(typescript@5.9.3)(zod@3.25.76) + typescript: + specifier: ^5.8.3 + version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.7(@types/node@22.7.5) + +packages: + + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@base-org/account@2.4.0': + resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} + + '@cartridge/connector@0.13.16': + resolution: {integrity: sha512-pXFRrWIqhb7GwNa4FJUr46Ke/v+68AyMKbeEwyoPgejtJQ2+WXSdJVE9E7AZ5v8EThLfCfHbQm7Ls0C9zZbe2g==} + peerDependencies: + '@starknet-react/core': ^5.0.1 + + '@cartridge/controller-wasm@0.10.1': + resolution: {integrity: sha512-NweHJ98lFrV103sT+ud0WOUT+j92bBdaTdT+cFRwPA+XmRB39UekMu35iWlFcBhlnxQF+GdX4G/IrQtfoXf9+A==} + + '@cartridge/controller@0.13.16': + resolution: {integrity: sha512-fb43enXNabH8jj2pIqBVNuzZE9NBEXuecbw9VfJxK1ARwF/fM0ZDdCFGHe7n4eD+vA4/P8W9v4tPE1alUIZaIw==} + peerDependencies: + react: ^18.2.0 || ^19.0.0 + react-dom: ^18.2.0 || ^19.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@cartridge/penpal@6.2.4': + resolution: {integrity: sha512-tdpOnSJJBFMlgLZ1+z9Ho5e6cG5EgMAb1Cmmh1lGT2tmplogU/XPMjLE6CwvKAPDoe6a38iMnbH+ySTAWWIOKA==} + + '@cbor-extract/cbor-extract-darwin-arm64@2.2.2': + resolution: {integrity: sha512-ZKZ/F8US7JR92J4DMct6cLW/Y66o2K576+zjlEN/MevH70bFIsB10wkZEQPLzl2oNh2SMGy55xpJ9JoBRl5DOA==} + cpu: [arm64] + os: [darwin] + + '@cbor-extract/cbor-extract-darwin-x64@2.2.2': + resolution: {integrity: sha512-32b1mgc+P61Js+KW9VZv/c+xRw5EfmOcPx990JbCBSkYJFY0l25VinvyyWfl+3KjibQmAcYwmyzKF9J4DyKP/Q==} + cpu: [x64] + os: [darwin] + + '@cbor-extract/cbor-extract-linux-arm64@2.2.2': + resolution: {integrity: sha512-wfqgzqCAy/Vn8i6WVIh7qZd0DdBFaWBjPdB6ma+Wihcjv0gHqD/mw3ouVv7kbbUNrab6dKEx/w3xQZEdeXIlzg==} + cpu: [arm64] + os: [linux] + + '@cbor-extract/cbor-extract-linux-arm@2.2.2': + resolution: {integrity: sha512-tNg0za41TpQfkhWjptD+0gSD2fggMiDCSacuIeELyb2xZhr7PrhPe5h66Jc67B/5dmpIhI2QOUtv4SBsricyYQ==} + cpu: [arm] + os: [linux] + + '@cbor-extract/cbor-extract-linux-x64@2.2.2': + resolution: {integrity: sha512-rpiLnVEsqtPJ+mXTdx1rfz4RtUGYIUg2rUAZgd1KjiC1SehYUSkJN7Yh+aVfSjvCGtVP0/bfkQkXpPXKbmSUaA==} + cpu: [x64] + os: [linux] + + '@cbor-extract/cbor-extract-win32-x64@2.2.2': + resolution: {integrity: sha512-dI+9P7cfWxkTQ+oE+7Aa6onEn92PHgfWXZivjNheCRmTBDBf2fx6RyTi0cmgpYLnD1KLZK9ZYrMxaPZ4oiXhGA==} + cpu: [x64] + os: [win32] + + '@coinbase/cdp-sdk@1.54.0': + resolution: {integrity: sha512-FfIJVEKAXgmr+dkXn/NBQO04/pps+oIgqEIdcmG67WZh+m07OsVnvSBEqEvbFO+VjSdaQAKu69EpO3NdHJd2Iw==} + peerDependencies: + '@x402/core': ^2.19.0 + '@x402/evm': ^2.19.0 + '@x402/extensions': ^2.19.0 + '@x402/svm': ^2.19.0 + peerDependenciesMeta: + '@x402/core': + optional: true + '@x402/evm': + optional: true + '@x402/extensions': + optional: true + '@x402/svm': + optional: true + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@hpke/chacha20poly1305@1.8.0': + resolution: {integrity: sha512-FcBfAQ+Y99vMNJP2yrZ9wpL8V0GOwp1+zMyzvc6alasrBygfFjFm1yeUtyADJCu/27C3Lm5mJzx6u7pwg+cX5w==} + engines: {node: '>=16.0.0'} + + '@hpke/common@1.10.1': + resolution: {integrity: sha512-moJwhmtLtuxiUzzNp1jpfBfx8yefKoO9D/RCR9dmwrnc7qjJqId1rEtQz+lSlU5cabX8daToMSx/7HayXOiaFw==} + engines: {node: '>=16.0.0'} + + '@hpke/core@1.9.0': + resolution: {integrity: sha512-pFxWl1nNJeQCSUFs7+GAblHvXBCjn9EPN65vdKlYQil2aURaRxfGMO6vBKGqm1YHTKwiAxJQNEI70PbSowMP9Q==} + engines: {node: '>=16.0.0'} + + '@hpke/dhkem-x25519@1.8.0': + resolution: {integrity: sha512-S1MWWkAfu+TFxySgv5+2P3O4Mx/jk7BsoplzQaA1s3sfUJVJ2UsZsSzSsMc+FXJumLXncoJFlO6mK6mDGspfmA==} + engines: {node: '>=16.0.0'} + + '@hpke/dhkem-x448@1.8.0': + resolution: {integrity: sha512-mFfnZfgp4OKkUIS/FKikfUgdnDKRy25ytCKBQiV+N+HbYy3I4v4ZCPBQ69QL+TYmKmCZJeUEnYeS5K+OBRP+Eg==} + engines: {node: '>=16.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@lit-labs/ssr-dom-shim@1.6.0': + resolution: {integrity: sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==} + + '@lit/react@1.0.8': + resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==} + peerDependencies: + '@types/react': 17 || 18 || 19 + + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + + '@module-federation/error-codes@0.12.0': + resolution: {integrity: sha512-DEXQjopcBuGzp/NA9OVtASO0uZ6grVK5TIe0PjrbDRyZDxVaYQXKrISxBLOE+3nSIELE98tYpfxptm8WC9A8zA==} + + '@module-federation/runtime-core@0.12.0': + resolution: {integrity: sha512-373zBM54196KHURs/O8lry9trCAM3PPidvsF4YdrtahNc8YaQynml0mE3zdZeBnqP6H0/4OpPqMMjACI80Ht8w==} + + '@module-federation/runtime@0.12.0': + resolution: {integrity: sha512-Cz9/7+gSvrdencwA8LXUMKnZdu0/flyN+yk6t3pkxfhvPJi3W65ZcalAKyOgyk2x8rEYrRSyEXu+/2DIFgrzmA==} + + '@module-federation/sdk@0.12.0': + resolution: {integrity: sha512-vh3GcG90fxjbkMghK7iSWcMayi/y8U5DxI6mhEFuz11St3y1UgQO2TZYephL8nISFBld7DdiqAkimx+6Hb3hjQ==} + + '@msgpack/msgpack@3.1.3': + resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==} + engines: {node: '>= 18'} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/curves@1.7.0': + resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.0': + resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.0': + resolution: {integrity: sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.6.0': + resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.6.1': + resolution: {integrity: sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.0': + resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@peculiar/asn1-cms@2.8.0': + resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} + + '@peculiar/asn1-csr@2.8.0': + resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==} + + '@peculiar/asn1-ecc@2.8.0': + resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==} + + '@peculiar/asn1-pfx@2.8.0': + resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==} + + '@peculiar/asn1-pkcs8@2.8.0': + resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==} + + '@peculiar/asn1-pkcs9@2.8.0': + resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==} + + '@peculiar/asn1-rsa@2.8.0': + resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==} + + '@peculiar/asn1-schema@2.8.0': + resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==} + + '@peculiar/asn1-x509-attr@2.8.0': + resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==} + + '@peculiar/asn1-x509@2.8.0': + resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.12.3': + resolution: {integrity: sha512-+Mzq+W7cNEKfkNZzyLl6A6ffqc3r21HGZUezgfKxpZrkORfOqgRXnS80Zu0IV6a9Ue9QBJeKD7kN0iWfc3bhRQ==} + + '@phosphor-icons/webcomponents@2.1.5': + resolution: {integrity: sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==} + + '@reown/appkit-common@1.8.19': + resolution: {integrity: sha512-z5wDrYjUGY7YbM4b14NHVo54WKZ5++PQtGkcsXhiOP39yAVijubBQD8BfHs/Pu2fSFqnqLIFoCVvIEfNWWccRw==} + + '@reown/appkit-controllers@1.8.19': + resolution: {integrity: sha512-JFNT8CfAVit9FJXh596Ye4U8A/oIapW+Y0KQqjB59DXyTCDZbxZDB32rULBQrSkZ6PufTEa239Dil4kABCQKtg==} + + '@reown/appkit-pay@1.8.19': + resolution: {integrity: sha512-HO/tQT0TbTQO3eONxNNPJAOZAOzUiHvjM0Mty1rFFeRBH68auiqQxQi2YFNMs014gNkRN+cb84VYau7+MCC0fQ==} + + '@reown/appkit-polyfills@1.8.19': + resolution: {integrity: sha512-PSoetRSuZg7f2YFPzdfs4BayQl51zcGqYr7frwOe6td0XEsspLrrVFn/zk5QFbFHZVsMdfRZ+TTunt84ozRdnQ==} + + '@reown/appkit-scaffold-ui@1.8.19': + resolution: {integrity: sha512-Ak767x0VzeDIXb0wbzkl19kx6udw7vkb1EU0SAweG3iKc9BunW87Rfcd48/YimzMZycJaYmlbtfmqQQDYs6Few==} + + '@reown/appkit-ui@1.8.19': + resolution: {integrity: sha512-fCAwW8yyyC3JcgKLBPvCtYuDGC4H8anO7u4LTaAXGEzdcU5H+IrCgNFSPNK7NuTSmgXm1TnoYxPxRFKNiNwFdA==} + + '@reown/appkit-utils@1.8.19': + resolution: {integrity: sha512-VQPgUMTFqoh4UD3EDZSw9wyMkyZsmIVmu8CdQ2FUxIuqYW4fLd0VIpkDeO64MMhSv8b0X8Vd6m4+eGcqSwlUAg==} + peerDependencies: + valtio: 2.1.7 + + '@reown/appkit-wallet@1.8.19': + resolution: {integrity: sha512-NVdIKceUhkXYtsG32925ctmVn0QJFNyDlr+mWheMLCEZ/IUPn+6aA53vTVaSUquhyeFxUXtrCOh3ln6v1tup5w==} + + '@reown/appkit@1.8.19': + resolution: {integrity: sha512-wB+xatkRbOy0AY1cZxxtcKzzPk3l3CTFulDbaISLVmZI6ZnQrOFuLnYc285zGsC6DB4d6bmwYUh89zcMLa4PvQ==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.62.3': + resolution: {integrity: sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.3': + resolution: {integrity: sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.3': + resolution: {integrity: sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.3': + resolution: {integrity: sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.3': + resolution: {integrity: sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.3': + resolution: {integrity: sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': + resolution: {integrity: sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.62.3': + resolution: {integrity: sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.62.3': + resolution: {integrity: sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.62.3': + resolution: {integrity: sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.62.3': + resolution: {integrity: sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.62.3': + resolution: {integrity: sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.62.3': + resolution: {integrity: sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.62.3': + resolution: {integrity: sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.62.3': + resolution: {integrity: sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.62.3': + resolution: {integrity: sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.62.3': + resolution: {integrity: sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.62.3': + resolution: {integrity: sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.62.3': + resolution: {integrity: sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.62.3': + resolution: {integrity: sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.3': + resolution: {integrity: sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.3': + resolution: {integrity: sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.3': + resolution: {integrity: sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.3': + resolution: {integrity: sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.3': + resolution: {integrity: sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==} + cpu: [x64] + os: [win32] + + '@safe-global/safe-apps-provider@0.18.6': + resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} + + '@safe-global/safe-apps-sdk@9.1.0': + resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==} + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': + resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==} + engines: {node: '>=16'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@scure/starknet@1.1.0': + resolution: {integrity: sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==} + + '@solana-program/system@0.10.0': + resolution: {integrity: sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana-program/token@0.9.0': + resolution: {integrity: sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana/accounts@5.5.1': + resolution: {integrity: sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/addresses@5.5.1': + resolution: {integrity: sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/assertions@5.5.1': + resolution: {integrity: sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-core@5.5.1': + resolution: {integrity: sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@5.5.1': + resolution: {integrity: sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@5.5.1': + resolution: {integrity: sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-strings@5.5.1': + resolution: {integrity: sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + + '@solana/codecs@5.5.1': + resolution: {integrity: sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/errors@5.5.1': + resolution: {integrity: sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fast-stable-stringify@5.5.1': + resolution: {integrity: sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/functional@5.5.1': + resolution: {integrity: sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instruction-plans@5.5.1': + resolution: {integrity: sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instructions@5.5.1': + resolution: {integrity: sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/keys@5.5.1': + resolution: {integrity: sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@5.5.1': + resolution: {integrity: sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/nominal-types@5.5.1': + resolution: {integrity: sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/offchain-messages@5.5.1': + resolution: {integrity: sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/options@5.5.1': + resolution: {integrity: sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@5.5.1': + resolution: {integrity: sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@5.5.1': + resolution: {integrity: sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@5.5.1': + resolution: {integrity: sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@5.5.1': + resolution: {integrity: sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-parsed-types@5.5.1': + resolution: {integrity: sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec-types@5.5.1': + resolution: {integrity: sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec@5.5.1': + resolution: {integrity: sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-api@5.5.1': + resolution: {integrity: sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1': + resolution: {integrity: sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-spec@5.5.1': + resolution: {integrity: sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions@5.5.1': + resolution: {integrity: sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transformers@5.5.1': + resolution: {integrity: sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transport-http@5.5.1': + resolution: {integrity: sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-types@5.5.1': + resolution: {integrity: sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc@5.5.1': + resolution: {integrity: sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/signers@5.5.1': + resolution: {integrity: sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/subscribable@5.5.1': + resolution: {integrity: sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/sysvars@5.5.1': + resolution: {integrity: sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-confirmation@5.5.1': + resolution: {integrity: sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-messages@5.5.1': + resolution: {integrity: sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transactions@5.5.1': + resolution: {integrity: sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@starknet-io/get-starknet-core@5.0.0': + resolution: {integrity: sha512-FVKYeNGHWG978482ZdCf7VQMoMn0xxHS0mWYbNB6zwkpo2yForHJwN4/EIHQkd2xp2mfAD1VcIHSZ4SLWGV1zw==} + + '@starknet-io/get-starknet-discovery@5.0.0': + resolution: {integrity: sha512-b40ZfhbisiEonxWLe/naOraZvNc1hfhy4TEs0TP/cYppfECDhBKncvvFTGu++/rm+xV3SjmSzwNQGQmIn5DMLQ==} + + '@starknet-io/get-starknet-virtual-wallet@5.0.0': + resolution: {integrity: sha512-ne9Svr0tHj7a5OJOvC82ewth/GJTa78Gal/iiiRzQmvIDpZcu+nzrPAi350RKEtErxjG5rZSPv2DLz7VBClN0Q==} + + '@starknet-io/get-starknet-wallet-standard@5.0.0': + resolution: {integrity: sha512-isDNGDlp16W24HE4IuweYXLDRZN0JbsDnazAieeKXE87Mn+jqhsjgTsMxcwWTjX7v906Bjz39FiDjGUddnr36g==} + + '@starknet-io/get-starknet-wallets@5.0.0': + resolution: {integrity: sha512-fuywFgoal4Al2KdFQCNTYgioK0ubu3cP+mM6OxE8ti78Nmd9ySlh78LwdKNXIp2Alq4kaQ5XMf6HMgkStCWhkw==} + + '@starknet-io/types-js@0.10.0': + resolution: {integrity: sha512-7ALSydz6pq3YIOpq5a7OkkxqwJciMc9Nlph0OGjhcC3xX0xH30XgizmziLyYVN10oO9+BJk8M9KbJjpzdbtRSw==} + + '@starknet-io/types-js@0.7.10': + resolution: {integrity: sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w==} + + '@starknet-io/types-js@0.8.4': + resolution: {integrity: sha512-0RZ3TZHcLsUTQaq1JhDSCM8chnzO4/XNsSCozwDET64JK5bjFDIf2ZUkta+tl5Nlbf4usoU7uZiDI/Q57kt2SQ==} + + '@starknet-io/types-js@0.9.1': + resolution: {integrity: sha512-ngLjOFuWOI4EFij8V+nl5tgHVACr6jqgLNUQbgD+AgnTcAN33SemBPXDIsovwK1Mz1U04Cz3qjDOnTq7067ZQw==} + + '@starknet-io/types-js@0.9.2': + resolution: {integrity: sha512-vWOc0FVSn+RmabozIEWcEny1I73nDGTvOrLYJsR1x7LGA3AZmqt4i/aW69o/3i2NN5CVP8Ok6G1ayRQJKye3Wg==} + + '@starknet-react/chains@5.0.3': + resolution: {integrity: sha512-TxP391OTWgaqujT9Hu1Siljye54IHjEXrgkUuhMfxzf9sCu5q42ZzZgu9hLm0NcJgg9Fx2t75J0WB6eYmQ26fw==} + + '@starknet-react/core@5.0.3': + resolution: {integrity: sha512-BxtcYL+SPqqDo1cpzfNYcYmSY4Ifjz1O5vq5vDdjx2iHEvHYzxBMjnyacJ+JsdRlQiuLNRGInooC/yzFkMrHAQ==} + peerDependencies: + get-starknet-core: ^4.0.0 + react: ^18.0 + starknet: ^8.1.2 + + '@tanstack/query-core@5.101.4': + resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} + + '@tanstack/react-query@5.101.4': + resolution: {integrity: sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==} + peerDependencies: + react: ^18 || ^19 + + '@turnkey/api-key-stamper@0.6.5': + resolution: {integrity: sha512-dSINHLIzYVw+ZNLwjM1wnEYFkYHPjQDSi43opCUhvow/ws6Fsn6gWkYpzbH+6Ej3rvSJTuaTwsld+dsTG/3HnA==} + engines: {node: '>=18.0.0'} + + '@turnkey/crypto@2.8.14': + resolution: {integrity: sha512-UZU+DEwhSUyyMHC9VZbp5av8NY/IJsfJ+g1E4dndQjMSAJd5NPKGS7sItlBy1ifN6wBb9JP5AcvmcfCdCfj9bw==} + engines: {node: '>=18.0.0'} + + '@turnkey/encoding@0.6.0': + resolution: {integrity: sha512-IC8qXvy36+iGAeiaVIuJvB35uU2Ld/RAWI/DRTKS+ttBej0GXhOn48Ouu5mlca4jt8ZEuwXmDVv74A8uBQclsA==} + engines: {node: '>=18.0.0'} + + '@turnkey/http@3.18.1': + resolution: {integrity: sha512-KXhDAtohx3PPRigdU0qjdgSTuomD0xl3mONly5hn8/jgrp0Gs7BKXv0OGiw/3Sj02IPmlza3SRWBKGtWyGwNmg==} + engines: {node: '>=18.0.0'} + + '@turnkey/iframe-stamper@2.11.0': + resolution: {integrity: sha512-Iyf4W4S4Hx/RVsXl07PkRTh3g5Ca+vpN6SdUbRy8lt9IQZAJel/vxAk9XMyB7utX9TJx+icSjNYHvC0XGqJ47A==} + engines: {node: '>=18.0.0'} + + '@turnkey/indexed-db-stamper@1.2.6': + resolution: {integrity: sha512-RSlCpH96e46PYgc029/TK3nE/HOW4QgKo9bXIYjoUR8J7+xkq+0Z0wjHrp27Yn3PbW8743hEppqK9jxsYc8ptA==} + engines: {node: '>=18.0.0'} + + '@turnkey/sdk-browser@5.16.1': + resolution: {integrity: sha512-AXBUJVW83nZKnSVNUH235dbsu4S+a4F0Swo25Fm/gHWKPVh4nvpT/Pr2cr6PVh3UR15p787AVUkxO9ZgtNclPw==} + engines: {node: '>=18.0.0'} + + '@turnkey/sdk-types@0.14.0': + resolution: {integrity: sha512-FLN4p3Jc3rBMvM17tgFrO4VpqkQN0RgWtLknqcqpLuNgTJ7oqgvzm42YIrzvBlW6DPsnFjVZMzc4yCfaRN7Lmg==} + engines: {node: '>=18.0.0'} + + '@turnkey/wallet-stamper@1.1.16': + resolution: {integrity: sha512-IdgoIRQr5RoiHAPnVag2QyaYg6cmmkZQs1rOig57/tQ/fXdPsC1+2660RisIAvv69ZFrArG8Z8wHaGSQMkStlw==} + + '@turnkey/webauthn-stamper@0.6.0': + resolution: {integrity: sha512-jdN17QEnn7RBykEOhtKIialWmDjnDAH8DzbyITwn8jsKcwT1TBNYge89hTUTjbdsDLBAqQw8cHujPdy0RaAqvw==} + engines: {node: '>=18.0.0'} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@22.7.5': + resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.31': + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@vitest/expect@3.2.7': + resolution: {integrity: sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==} + + '@vitest/mocker@3.2.7': + resolution: {integrity: sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.7': + resolution: {integrity: sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==} + + '@vitest/runner@3.2.7': + resolution: {integrity: sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==} + + '@vitest/snapshot@3.2.7': + resolution: {integrity: sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==} + + '@vitest/spy@3.2.7': + resolution: {integrity: sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==} + + '@vitest/utils@3.2.7': + resolution: {integrity: sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==} + + '@wallet-standard/base@1.1.1': + resolution: {integrity: sha512-gggIHTtxicF9XFMQ12DkfS6NAG92Ak795JeSA7f2whAQ6Y3AkMWWuCMxSZXG2NIPN42kEaZSNVjqMsJRaJRxMQ==} + engines: {node: '>=22'} + + '@wallet-standard/features@1.1.1': + resolution: {integrity: sha512-aCWYmVeSCGViyEU5k7GMoW8zxE4Gs+C1s1Pp2XLesvSNlnZ4PMES9HUnTB3hl0b3RVj7C61yze3IWyrncqg4MA==} + engines: {node: '>=22'} + + '@wallet-standard/wallet@1.1.0': + resolution: {integrity: sha512-Gt8TnSlDZpAl+RWOOAB/kuvC7RpcdWAlFbHNoi4gsXsfaWa1QCT6LBcfIYTPdOZC9OVZUDwqGuGAcqZejDmHjg==} + engines: {node: '>=16'} + + '@wallet-standard/wallet@1.1.1': + resolution: {integrity: sha512-8WiRPaKk/wNNRZhB2eVhpR/JW7/aqTCMoZhgVUCujuzDmxxmGvsosMxdCG4NAdYkoyozAHCX8/xLtlWUn5mNdQ==} + engines: {node: '>=22'} + + '@walletconnect/core@2.23.10': + resolution: {integrity: sha512-Qq2btHEoCgruvkZCWLSrVsvg/dYbM9Z045qeClwhJR4meL32jbIRT0mKWjf0HkRc2LA82MsnszVnfuZl3yWl5A==} + engines: {node: '>=18.20.8'} + + '@walletconnect/core@2.23.7': + resolution: {integrity: sha512-yTyymn9mFaDZkUfLfZ3E9VyaSDPeHAXlrPxQRmNx2zFsEt/25GmTU2A848aomimLxZnAG2jNLhxbJ8I0gyNV+w==} + engines: {node: '>=18.20.8'} + + '@walletconnect/environment@1.0.1': + resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + + '@walletconnect/ethereum-provider@2.23.10': + resolution: {integrity: sha512-OpmTS2s+zrqK7cr8yfGu62tCv6Dsoe/TI1SFYQd2tKyAaBYjrKVzZPfqbRYesgbtNKcCn8KK5Y6PrKIK52U7RQ==} + + '@walletconnect/events@1.0.1': + resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + + '@walletconnect/heartbeat@1.2.2': + resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} + + '@walletconnect/jsonrpc-http-connection@1.0.8': + resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} + + '@walletconnect/jsonrpc-provider@1.0.14': + resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + + '@walletconnect/jsonrpc-types@1.0.4': + resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} + + '@walletconnect/jsonrpc-utils@1.0.8': + resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} + + '@walletconnect/keyvaluestorage@1.1.1': + resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + peerDependencies: + '@react-native-async-storage/async-storage': 1.x + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@walletconnect/logger@3.0.2': + resolution: {integrity: sha512-7wR3wAwJTOmX4gbcUZcFMov8fjftY05+5cO/d4cpDD8wDzJ+cIlKdYOXaXfxHLSYeDazMXIsxMYjHYVDfkx+nA==} + + '@walletconnect/relay-api@1.0.11': + resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} + + '@walletconnect/relay-auth@1.1.0': + resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} + + '@walletconnect/safe-json@1.0.2': + resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + + '@walletconnect/sign-client@2.23.10': + resolution: {integrity: sha512-vO7DGRRmKo+rykmjVyQR1aM4I2nbk9kJ6olbxgjFRR6Jdhy+Kz+zgN7Ce5xVhPfWYVu4bV/XhOQxhvnQw7S5ng==} + + '@walletconnect/sign-client@2.23.7': + resolution: {integrity: sha512-SX61lzb1bTl/LijlcHQttnoHPBzzoY5mW9ArR6qhFtDNDTS7yr2rcH7rCngxHlYeb4rAYcWLHgbiGSrdKxl/mg==} + + '@walletconnect/time@1.0.2': + resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + + '@walletconnect/types@2.23.10': + resolution: {integrity: sha512-XP8d41979anTrc1OJF3ISF+g81cvp1wim+ObdNnbcaT/jhwLwv+0T7rRe9VwRv+h8EaRgLyeb5YGy7oJ49vxVg==} + + '@walletconnect/types@2.23.7': + resolution: {integrity: sha512-6PAKK+iR2IntmlkCFLMAHjYeIaerCJJYRDmdRimhon0u+aNmQT+HyGM6zxDAth0rdpBD7qEvKP5IXZTE7KFUhw==} + + '@walletconnect/universal-provider@2.23.10': + resolution: {integrity: sha512-wkLcoaPA8R1Cfx5dcYmS7oMalYd0aEO6+8PNs9gZ5Zwe+zwU+6HOImL8HcCHngeMcdjLqW5aKN3Dy6NbueA3fg==} + + '@walletconnect/universal-provider@2.23.7': + resolution: {integrity: sha512-6UicU/Mhr/1bh7MNoajypz7BhigORbHpP1LFTf8FYLQGDqzmqHMqmMH2GDAImtaY2sFTi2jBvc22tLl8VMze/A==} + + '@walletconnect/utils@2.23.10': + resolution: {integrity: sha512-b1c9FRF2g7vNnz66oLW5WZD2VCMrbu9xhpmwJJwqGarBiGW7cY8NbUtS9/w2/qc0vsBVKJ/bzDn4TGjpELU6aQ==} + + '@walletconnect/utils@2.23.7': + resolution: {integrity: sha512-3p38gNrkVcIiQixVrlsWSa66Gjs5PqHOug2TxDgYUVBW5NcKjwQA08GkC6CKBQUfr5iaCtbfy6uZJW1LKSIvWQ==} + + '@walletconnect/window-getters@1.0.1': + resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} + + '@walletconnect/window-metadata@1.0.1': + resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + + abi-wan-kanabi@2.2.4: + resolution: {integrity: sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg==} + hasBin: true + + abitype@1.0.6: + resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.3.0: + resolution: {integrity: sha512-fk6Te+bojIFrMvMZrnOO+SxCB+RUksTGOzq/60ZRvs1L+BVzvi2bqt9L3W/17ZLdZsyM1FuYf65P5nlmoiH1Bg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansicolors@0.3.2: + resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + axios-retry@4.5.0: + resolution: {integrity: sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==} + peerDependencies: + axios: 0.x || 1.x + + axios@1.16.0: + resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} + + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.11.9: + resolution: {integrity: sha512-cp447VUsGS07+n1Dqf7YSQ8maeJrjEhaDxTm1ZefbqDtypHBC5GzGMQbklR6IPR13Y8OAJRHZWEMtZipJLCttg==} + engines: {node: '>=6.0.0'} + hasBin: true + + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + + blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + + borsh@2.0.0: + resolution: {integrity: sha512-kc9+BgR3zz9+cjbwM8ODoUB4fs3X3I5A/HtX7LZKxCLaMrEeDFoBpnhZY//DTS1VZBSs6S5v46RZRbZjRFspEg==} + + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + bs58check@4.0.0: + resolution: {integrity: sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + + cardinal@2.1.1: + resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} + hasBin: true + + cbor-extract@2.2.2: + resolution: {integrity: sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng==} + hasBin: true + + cbor-js@0.1.0: + resolution: {integrity: sha512-7sQ/TvDZPl7csT1Sif9G0+MA0I0JOVah8+wWlJVQdVEgIbCzlN/ab3x+uvMNsc34TUvO6osQTAmB2ls80JX6tw==} + + cbor-x@1.6.5: + resolution: {integrity: sha512-yO64CxnSh6kp+pHNRK9IfwnMvCB+c8HvmUjQY/9l9YRF0/cAPka/tUHLwS64QqUpFCq3/OtbKziVJYXH2EaRig==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.399: + resolution: {integrity: sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encode-utf8@1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.44.0: + resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==} + + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + ethers@6.17.0: + resolution: {integrity: sha512-BpyrpIPJ3ydEVow8zGaz1DuPS7YU8DcWxuBnY9a0UA/lvAPwrMr+EPXsfrul628SRaekPNeIM4UFh/91GWZang==} + engines: {node: '>=14.0.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-starknet-core@4.0.0: + resolution: {integrity: sha512-6pLmidQZkC3wZsrHY99grQHoGpuuXqkbSP65F8ov1/JsEI8DDLkhsAuLCKFzNOK56cJp+f1bWWfTJ57e9r5eqQ==} + deprecated: Package no longer supported. Please use @starknet-io/get-starknet-core + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hpke-js@1.8.0: + resolution: {integrity: sha512-N0PFQlUQsIPS9++nUNn2ZsxTPSv8pONyyrXIGZl0iiherRfS0XW1SvTd+RmepD0TN1S9zzTJkEutMIWWYt0/4w==} + engines: {node: '>=16.0.0'} + + idb-keyval@6.2.1: + resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} + + idb-keyval@6.3.0: + resolution: {integrity: sha512-um+2dgAWmYsu615EXpWVwSmapJhON0G43t3Ka/EVaohzPQXSMqKEqeDK/oIW3Ow+BXaF2PvSc+oBTFp793A5Ow==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-retry-allowed@2.2.0: + resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + engines: {node: '>=10'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + + jose@6.2.6: + resolution: {integrity: sha512-HwMtbJjMw8rC8dUTwCNilHJD+fxTeKM3JV1eprSmTjS41qwXSSt6exJXgyPK1QOu0jB9eDYLESRDkB3qaT3jnw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + keyvaluestorage-interface@1.0.0: + resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} + + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + + lit-html@3.3.3: + resolution: {integrity: sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==} + + lit@3.3.0: + resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lossless-json@4.3.1: + resolution: {integrity: sha512-SqD/Bg3ZfltBJ2Z14hJ/BihnvtV553WO4g9/ePtlp4lrnl9jF3AdIJt53A/Wkg/0Li+LMfxaBqgx1MiFZdQlpQ==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + + micro-packed@0.7.3: + resolution: {integrity: sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==} + + micro-sol-signer@0.5.0: + resolution: {integrity: sha512-4D5mGHuWuAC1w7HXXs+mlugw9n0hk3Gk0pFjQYkzE6zbvwEEioyb6l8375hivcqD6830/tCDCyQv+i3jfVFC+w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mipd@0.0.7: + resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build-optional-packages@5.1.1: + resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} + hasBin: true + + node-mock-http@1.0.5: + resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==} + + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + ox@0.14.33: + resolution: {integrity: sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.4.4: + resolution: {integrity: sha512-oJPEeCDs9iNiPs6J0rTx+Y0KGeCGyCAA3zo94yZhm8G5WpOxrwUtn2Ie/Y8IyARSqqY/j9JTKA3Fc1xs1DvFnw==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.3: + resolution: {integrity: sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + pako@2.2.0: + resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.0.0: + resolution: {integrity: sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==} + hasBin: true + + playwright-core@1.62.1: + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.62.1: + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==} + engines: {node: '>=20'} + hasBin: true + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + + process-warning@5.1.0: + resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==} + + proxy-compare@3.0.1: + resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + + qrcode@1.5.3: + resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} + engines: {node: '>=10.13.0'} + hasBin: true + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + redeyed@2.1.1: + resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + rollup@4.62.3: + resolution: {integrity: sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + sha256-uint8array@0.10.7: + resolution: {integrity: sha512-1Q6JQU4tX9NqsDGodej6pkrUVQVNapLZnvkwIhddH/JqzBZF1fSaxSWNY6sziXBE8aEa2twtGkXUrwzGeZCMpQ==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + slow-redact@0.3.2: + resolution: {integrity: sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==} + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + starknet@8.9.2: + resolution: {integrity: sha512-+dp+o2w67fV6JyVOVkYeM1Ec71aORHc/JrF4VHLlfeGee0nLilooCQLE2u6hUcSGQG2x2/fvzkxYpIN+k1JBvA==} + engines: {node: '>=22'} + + starknet@9.4.2: + resolution: {integrity: sha512-NFtg077DjddHUSh8sLZ5uB149LEF4NR90FuJ0oF7+zhce7l0oG9Ubqr3H4+jHm/ghHtV+yjlv1UhEoo4SBfILA==} + engines: {node: '>=22'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + thread-stream@3.2.0: + resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-mixer@6.0.4: + resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + uint8arrays@3.1.1: + resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici-types@7.29.0: + resolution: {integrity: sha512-vamA8dGlzMwhpyYpQp9d8vka3o4D/yn5I7ez7Or+msDA4bZ8Uh+Zy91WvWf3I73gDAkFha9JcYRqm2li0Npfgg==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + valtio@2.1.7: + resolution: {integrity: sha512-DwJhCDpujuQuKdJ2H84VbTjEJJteaSmqsuUltsfbfdbotVfNeTE4K/qc/Wi57I9x8/2ed4JNdjEna7O6PfavRg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + react: '>=18.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + viem@2.55.10: + resolution: {integrity: sha512-Q9Ba+/ma81U2M5o5P2AQ7Ux8rTIwmCZvUcr8rKdQ22bV0IBFHllM2m5gWDP8hFaUN2nH2oW3QG44amRazflYNQ==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@3.2.7: + resolution: {integrity: sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.7 + '@vitest/ui': 3.2.7 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + ws@7.5.13: + resolution: {integrity: sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.1: + resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zustand@5.0.3: + resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@adraffy/ens-normalize@1.11.1': {} + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.7 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@base-org/account@2.4.0(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.54.0(typescript@5.9.3) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + zustand: 5.0.3(@types/react@18.3.31)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + optional: true + + '@cartridge/connector@0.13.16(@starknet-react/core@5.0.3(get-starknet-core@4.0.0)(react@18.3.1)(starknet@9.4.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@cartridge/controller': 0.13.16(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@starknet-react/core': 5.0.3(get-starknet-core@4.0.0)(react@18.3.1)(starknet@9.4.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - react-dom + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@cartridge/controller-wasm@0.10.1': {} + + '@cartridge/controller@0.13.16(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@cartridge/controller-wasm': 0.10.1 + '@cartridge/penpal': 6.2.4 + '@starknet-io/get-starknet-core': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/types-js': 0.9.1 + '@turnkey/sdk-browser': 5.16.1(typescript@5.9.3)(zod@3.25.76) + '@wallet-standard/wallet': 1.1.1 + '@walletconnect/ethereum-provider': 2.23.10(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + bs58: 6.0.0 + cbor-x: 1.6.5 + ethers: 6.17.0 + micro-sol-signer: 0.5.0 + mipd: 0.0.7(typescript@5.9.3) + open: 10.2.0 + starknet: 8.9.2 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@cartridge/penpal@6.2.4': {} + + '@cbor-extract/cbor-extract-darwin-arm64@2.2.2': + optional: true + + '@cbor-extract/cbor-extract-darwin-x64@2.2.2': + optional: true + + '@cbor-extract/cbor-extract-linux-arm64@2.2.2': + optional: true + + '@cbor-extract/cbor-extract-linux-arm@2.2.2': + optional: true + + '@cbor-extract/cbor-extract-linux-x64@2.2.2': + optional: true + + '@cbor-extract/cbor-extract-win32-x64@2.2.2': + optional: true + + '@coinbase/cdp-sdk@1.54.0(typescript@5.9.3)': + dependencies: + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(typescript@5.9.3)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(typescript@5.9.3)) + '@solana/kit': 5.5.1(typescript@5.9.3) + abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) + axios: 1.16.0 + axios-retry: 4.5.0(axios@1.16.0) + bs58: 6.0.0 + jose: 6.2.6 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + optional: true + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@hpke/chacha20poly1305@1.8.0': + dependencies: + '@hpke/common': 1.10.1 + + '@hpke/common@1.10.1': {} + + '@hpke/core@1.9.0': + dependencies: + '@hpke/common': 1.10.1 + + '@hpke/dhkem-x25519@1.8.0': + dependencies: + '@hpke/common': 1.10.1 + + '@hpke/dhkem-x448@1.8.0': + dependencies: + '@hpke/common': 1.10.1 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@lit-labs/ssr-dom-shim@1.6.0': {} + + '@lit/react@1.0.8(@types/react@18.3.31)': + dependencies: + '@types/react': 18.3.31 + optional: true + + '@lit/reactive-element@2.1.2': + dependencies: + '@lit-labs/ssr-dom-shim': 1.6.0 + + '@module-federation/error-codes@0.12.0': {} + + '@module-federation/runtime-core@0.12.0': + dependencies: + '@module-federation/error-codes': 0.12.0 + '@module-federation/sdk': 0.12.0 + + '@module-federation/runtime@0.12.0': + dependencies: + '@module-federation/error-codes': 0.12.0 + '@module-federation/runtime-core': 0.12.0 + '@module-federation/sdk': 0.12.0 + + '@module-federation/sdk@0.12.0': {} + + '@msgpack/msgpack@3.1.3': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/curves@1.7.0': + dependencies: + '@noble/hashes': 1.6.0 + + '@noble/curves@1.8.0': + dependencies: + '@noble/hashes': 1.7.0 + + '@noble/curves@1.9.0': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.3.2': {} + + '@noble/hashes@1.4.0': + optional: true + + '@noble/hashes@1.6.0': {} + + '@noble/hashes@1.6.1': {} + + '@noble/hashes@1.7.0': {} + + '@noble/hashes@1.8.0': {} + + '@peculiar/asn1-cms@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pfx': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.8.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.12.3': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-csr': 2.8.0 + '@peculiar/asn1-ecc': 2.8.0 + '@peculiar/asn1-pkcs9': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + + '@phosphor-icons/webcomponents@2.1.5': + dependencies: + lit: 3.3.0 + + '@reown/appkit-common@1.8.19(typescript@5.9.3)(zod@3.22.4)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.55.10(typescript@5.9.3)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-common@1.8.19(typescript@5.9.3)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.19(typescript@5.9.3) + '@walletconnect/universal-provider': 2.23.7(typescript@5.9.3)(zod@3.25.76) + valtio: 2.1.7(@types/react@18.3.31)(react@18.3.1) + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-pay@1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-ui': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-utils': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(valtio@2.1.7(@types/react@18.3.31)(react@18.3.1))(zod@3.25.76) + lit: 3.3.0 + valtio: 2.1.7(@types/react@18.3.31)(react@18.3.1) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-polyfills@1.8.19': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-scaffold-ui@1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(valtio@2.1.7(@types/react@18.3.31)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-pay': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-ui': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-utils': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(valtio@2.1.7(@types/react@18.3.31)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.19(typescript@5.9.3) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - valtio + - zod + + '@reown/appkit-ui@1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@phosphor-icons/webcomponents': 2.1.5 + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-wallet': 1.8.19(typescript@5.9.3) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(valtio@2.1.7(@types/react@18.3.31)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.19 + '@reown/appkit-wallet': 1.8.19(typescript@5.9.3) + '@wallet-standard/wallet': 1.1.0 + '@walletconnect/logger': 3.0.2 + '@walletconnect/universal-provider': 2.23.7(typescript@5.9.3)(zod@3.25.76) + valtio: 2.1.7(@types/react@18.3.31)(react@18.3.1) + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + '@base-org/account': 2.4.0(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(typescript@5.9.3)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@reown/appkit-wallet@1.8.19(typescript@5.9.3)': + dependencies: + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.22.4) + '@reown/appkit-polyfills': 1.8.19 + '@walletconnect/logger': 3.0.2 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@reown/appkit@1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.8.19(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-controllers': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-pay': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-polyfills': 1.8.19 + '@reown/appkit-scaffold-ui': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(valtio@2.1.7(@types/react@18.3.31)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-ui': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@reown/appkit-utils': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(valtio@2.1.7(@types/react@18.3.31)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.8.19(typescript@5.9.3) + '@walletconnect/universal-provider': 2.23.7(typescript@5.9.3)(zod@3.25.76) + bs58: 6.0.0 + semver: 7.7.2 + valtio: 2.1.7(@types/react@18.3.31)(react@18.3.1) + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + '@lit/react': 1.0.8(@types/react@18.3.31) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.62.3': + optional: true + + '@rollup/rollup-android-arm64@4.62.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.3': + optional: true + + '@rollup/rollup-darwin-x64@4.62.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.3': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.3': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.3': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.3': + optional: true + + '@safe-global/safe-apps-provider@0.18.6(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(typescript@5.9.3)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + + '@safe-global/safe-apps-sdk@9.1.0(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': + optional: true + + '@scure/base@1.2.6': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/starknet@1.1.0': + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + + '@solana-program/system@0.10.0(@solana/kit@5.5.1(typescript@5.9.3))': + dependencies: + '@solana/kit': 5.5.1(typescript@5.9.3) + optional: true + + '@solana-program/token@0.9.0(@solana/kit@5.5.1(typescript@5.9.3))': + dependencies: + '@solana/kit': 5.5.1(typescript@5.9.3) + optional: true + + '@solana/accounts@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/addresses@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/assertions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-core@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-data-structures@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-numbers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs-strings@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/codecs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/options': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/errors@5.5.1(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 14.0.2 + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/fast-stable-stringify@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/functional@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/instruction-plans@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/instructions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/keys@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/kit@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instruction-plans': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/plugin-core': 5.5.1(typescript@5.9.3) + '@solana/programs': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/signers': 5.5.1(typescript@5.9.3) + '@solana/sysvars': 5.5.1(typescript@5.9.3) + '@solana/transaction-confirmation': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + + '@solana/nominal-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/offchain-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/options@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/plugin-core@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/programs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/promises@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-parsed-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-spec-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-subscriptions-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + ws: 8.21.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-subscriptions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + + '@solana/rpc-transformers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-transport-http@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + undici-types: 7.29.0 + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/rpc-types@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-transport-http': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/signers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/subscribable@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + optional: true + + '@solana/sysvars@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/transaction-confirmation@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + optional: true + + '@solana/transaction-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/transactions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@starknet-io/get-starknet-core@5.0.0(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@starknet-io/get-starknet-discovery': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/get-starknet-virtual-wallet': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/get-starknet-wallet-standard': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/get-starknet-wallets': 5.0.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@starknet-io/get-starknet-discovery@5.0.0(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@starknet-io/get-starknet-virtual-wallet': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/get-starknet-wallet-standard': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/types-js': 0.7.10 + '@wallet-standard/base': 1.1.1 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@starknet-io/get-starknet-virtual-wallet@5.0.0(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@module-federation/runtime': 0.12.0 + '@starknet-io/get-starknet-wallet-standard': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/types-js': 0.7.10 + '@wallet-standard/base': 1.1.1 + '@wallet-standard/features': 1.1.1 + async-mutex: 0.5.0 + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@starknet-io/get-starknet-wallet-standard@5.0.0(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@starknet-io/types-js': 0.7.10 + '@wallet-standard/base': 1.1.1 + '@wallet-standard/features': 1.1.1 + ox: 0.4.4(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - typescript + - zod + + '@starknet-io/get-starknet-wallets@5.0.0': {} + + '@starknet-io/types-js@0.10.0': {} + + '@starknet-io/types-js@0.7.10': {} + + '@starknet-io/types-js@0.8.4': {} + + '@starknet-io/types-js@0.9.1': {} + + '@starknet-io/types-js@0.9.2': {} + + '@starknet-react/chains@5.0.3': {} + + '@starknet-react/core@5.0.3(get-starknet-core@4.0.0)(react@18.3.1)(starknet@9.4.2(typescript@5.9.3)(zod@3.25.76))(typescript@5.9.3)': + dependencies: + '@starknet-io/types-js': 0.7.10 + '@starknet-react/chains': 5.0.3 + '@tanstack/react-query': 5.101.4(react@18.3.1) + abi-wan-kanabi: 2.2.4 + eventemitter3: 5.0.4 + get-starknet-core: 4.0.0 + react: 18.3.1 + starknet: 9.4.2(typescript@5.9.3)(zod@3.25.76) + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@tanstack/query-core@5.101.4': {} + + '@tanstack/react-query@5.101.4(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.101.4 + react: 18.3.1 + + '@turnkey/api-key-stamper@0.6.5': + dependencies: + '@noble/curves': 1.9.7 + '@turnkey/crypto': 2.8.14 + '@turnkey/encoding': 0.6.0 + sha256-uint8array: 0.10.7 + + '@turnkey/crypto@2.8.14': + dependencies: + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.0 + '@noble/hashes': 1.8.0 + '@peculiar/x509': 1.12.3 + '@turnkey/encoding': 0.6.0 + '@turnkey/sdk-types': 0.14.0 + borsh: 2.0.0 + cbor-js: 0.1.0 + + '@turnkey/encoding@0.6.0': + dependencies: + bs58: 6.0.0 + bs58check: 4.0.0 + + '@turnkey/http@3.18.1': + dependencies: + '@turnkey/api-key-stamper': 0.6.5 + '@turnkey/encoding': 0.6.0 + '@turnkey/webauthn-stamper': 0.6.0 + cross-fetch: 3.2.0 + transitivePeerDependencies: + - encoding + + '@turnkey/iframe-stamper@2.11.0': {} + + '@turnkey/indexed-db-stamper@1.2.6': + dependencies: + '@turnkey/api-key-stamper': 0.6.5 + '@turnkey/encoding': 0.6.0 + + '@turnkey/sdk-browser@5.16.1(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@turnkey/api-key-stamper': 0.6.5 + '@turnkey/crypto': 2.8.14 + '@turnkey/encoding': 0.6.0 + '@turnkey/http': 3.18.1 + '@turnkey/iframe-stamper': 2.11.0 + '@turnkey/indexed-db-stamper': 1.2.6 + '@turnkey/sdk-types': 0.14.0 + '@turnkey/wallet-stamper': 1.1.16(typescript@5.9.3)(zod@3.25.76) + '@turnkey/webauthn-stamper': 0.6.0 + buffer: 6.0.3 + cross-fetch: 3.2.0 + hpke-js: 1.8.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + '@turnkey/sdk-types@0.14.0': {} + + '@turnkey/wallet-stamper@1.1.16(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@turnkey/crypto': 2.8.14 + '@turnkey/encoding': 0.6.0 + optionalDependencies: + viem: 2.55.10(typescript@5.9.3)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@turnkey/webauthn-stamper@0.6.0': + dependencies: + sha256-uint8array: 0.10.7 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.31)': + dependencies: + '@types/react': 18.3.31 + + '@types/react@18.3.31': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': {} + + '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.7.5))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.7': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.7 + '@vitest/utils': 3.2.7 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.7(vite@5.4.21(@types/node@22.7.5))': + dependencies: + '@vitest/spy': 3.2.7 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.7.5) + + '@vitest/pretty-format@3.2.7': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.7': + dependencies: + '@vitest/utils': 3.2.7 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.7': + dependencies: + '@vitest/pretty-format': 3.2.7 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.7': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.7': + dependencies: + '@vitest/pretty-format': 3.2.7 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@wallet-standard/base@1.1.1': {} + + '@wallet-standard/features@1.1.1': + dependencies: + '@wallet-standard/base': 1.1.1 + + '@wallet-standard/wallet@1.1.0': + dependencies: + '@wallet-standard/base': 1.1.1 + + '@wallet-standard/wallet@1.1.1': + dependencies: + '@wallet-standard/base': 1.1.1 + + '@walletconnect/core@2.23.10(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.10 + '@walletconnect/utils': 2.23.10(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.45.1 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/core@2.23.7(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.44.0 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/environment@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/ethereum-provider@2.23.10(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@reown/appkit': 1.8.19(@types/react@18.3.31)(react@18.3.1)(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.23.10(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/types': 2.23.10 + '@walletconnect/universal-provider': 2.23.10(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/utils': 2.23.10(typescript@5.9.3)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@x402/core' + - '@x402/evm' + - '@x402/extensions' + - '@x402/svm' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - typescript + - uploadthing + - use-sync-external-store + - utf-8-validate + - zod + + '@walletconnect/events@1.0.1': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + + '@walletconnect/heartbeat@1.2.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-http-connection@1.0.8': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + cross-fetch: 3.2.0 + events: 3.3.0 + transitivePeerDependencies: + - encoding + + '@walletconnect/jsonrpc-provider@1.0.14': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-types@1.0.4': + dependencies: + events: 3.3.0 + keyvaluestorage-interface: 1.0.0 + + '@walletconnect/jsonrpc-utils@1.0.8': + dependencies: + '@walletconnect/environment': 1.0.1 + '@walletconnect/jsonrpc-types': 1.0.4 + tslib: 1.14.1 + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.13 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@walletconnect/keyvaluestorage@1.1.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + idb-keyval: 6.3.0 + unstorage: 1.17.5(idb-keyval@6.3.0) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/logger@3.0.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 10.0.0 + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.1 + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/sign-client@2.23.10(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.10(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.10 + '@walletconnect/utils': 2.23.10(typescript@5.9.3)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.23.7(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.23.7(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.9.3)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/time@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/types@2.23.10': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.23.7': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/universal-provider@2.23.10(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.10(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/types': 2.23.10 + '@walletconnect/utils': 2.23.10(typescript@5.9.3)(zod@3.25.76) + es-toolkit: 1.45.1 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/universal-provider@2.23.7(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/sign-client': 2.23.7(typescript@5.9.3)(zod@3.25.76) + '@walletconnect/types': 2.23.7 + '@walletconnect/utils': 2.23.7(typescript@5.9.3)(zod@3.25.76) + es-toolkit: 1.44.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.23.10(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@msgpack/msgpack': 3.1.3 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.10 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + blakejs: 1.2.1 + detect-browser: 5.3.0 + ox: 0.9.3(typescript@5.9.3)(zod@3.25.76) + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - typescript + - uploadthing + - zod + + '@walletconnect/utils@2.23.7(typescript@5.9.3)(zod@3.25.76)': + dependencies: + '@msgpack/msgpack': 3.1.3 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.7 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + blakejs: 1.2.1 + detect-browser: 5.3.0 + ox: 0.9.3(typescript@5.9.3)(zod@3.25.76) + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - typescript + - uploadthing + - zod + + '@walletconnect/window-getters@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/window-metadata@1.0.1': + dependencies: + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + + abi-wan-kanabi@2.2.4: + dependencies: + ansicolors: 0.3.2 + cardinal: 2.1.1 + fs-extra: 10.1.0 + yargs: 17.7.3 + + abitype@1.0.6(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + optional: true + + abitype@1.2.3(typescript@5.9.3)(zod@3.22.4): + optionalDependencies: + typescript: 5.9.3 + zod: 3.22.4 + + abitype@1.2.3(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.3.0(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + aes-js@4.0.0-beta.5: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansicolors@0.3.2: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + assertion-error@2.0.1: {} + + async-mutex@0.5.0: + dependencies: + tslib: 2.8.1 + + asynckit@0.4.0: + optional: true + + atomic-sleep@1.0.0: {} + + axios-retry@4.5.0(axios@1.16.0): + dependencies: + axios: 1.16.0 + is-retry-allowed: 2.2.0 + optional: true + + axios@1.16.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + optional: true + + base-x@5.0.1: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.11.9: {} + + big.js@6.2.2: {} + + blakejs@1.2.1: {} + + borsh@2.0.0: {} + + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.9 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.399 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.7) + + bs58@6.0.0: + dependencies: + base-x: 5.0.1 + + bs58check@4.0.0: + dependencies: + '@noble/hashes': 1.8.0 + bs58: 6.0.0 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + optional: true + + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001806: {} + + cardinal@2.1.1: + dependencies: + ansicolors: 0.3.2 + redeyed: 2.1.1 + + cbor-extract@2.2.2: + dependencies: + node-gyp-build-optional-packages: 5.1.1 + optionalDependencies: + '@cbor-extract/cbor-extract-darwin-arm64': 2.2.2 + '@cbor-extract/cbor-extract-darwin-x64': 2.2.2 + '@cbor-extract/cbor-extract-linux-arm': 2.2.2 + '@cbor-extract/cbor-extract-linux-arm64': 2.2.2 + '@cbor-extract/cbor-extract-linux-x64': 2.2.2 + '@cbor-extract/cbor-extract-win32-x64': 2.2.2 + optional: true + + cbor-js@0.1.0: {} + + cbor-x@1.6.5: + optionalDependencies: + cbor-extract: 2.2.2 + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@5.6.2: + optional: true + + charenc@0.0.2: + optional: true + + check-error@2.1.3: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@1.2.1: + optional: true + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + optional: true + + commander@14.0.2: + optional: true + + convert-source-map@2.0.0: {} + + cookie-es@1.2.3: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + crypt@0.0.2: + optional: true + + csstype@3.2.3: {} + + dayjs@1.11.13: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + deep-eql@5.0.2: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-lazy-prop@3.0.0: {} + + defu@6.1.7: {} + + delayed-stream@1.0.0: + optional: true + + destr@2.0.5: {} + + detect-browser@5.3.0: {} + + detect-libc@2.1.2: + optional: true + + dijkstrajs@1.0.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + optional: true + + electron-to-chromium@1.5.399: {} + + emoji-regex@8.0.0: {} + + encode-utf8@1.0.3: {} + + es-define-property@1.0.1: + optional: true + + es-errors@1.3.0: + optional: true + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + optional: true + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + optional: true + + es-toolkit@1.44.0: {} + + es-toolkit@1.45.1: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + esprima@4.0.1: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + ethers@6.17.0: + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + eventemitter3@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + follow-redirects@1.16.0: + optional: true + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + optional: true + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: + optional: true + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + optional: true + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + optional: true + + get-starknet-core@4.0.0: + dependencies: + '@starknet-io/types-js': 0.7.10 + + gopd@1.2.0: + optional: true + + graceful-fs@4.2.11: {} + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.5 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + has-symbols@1.1.0: + optional: true + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + optional: true + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + optional: true + + hpke-js@1.8.0: + dependencies: + '@hpke/chacha20poly1305': 1.8.0 + '@hpke/common': 1.10.1 + '@hpke/core': 1.9.0 + '@hpke/dhkem-x25519': 1.8.0 + '@hpke/dhkem-x448': 1.8.0 + + idb-keyval@6.2.1: + optional: true + + idb-keyval@6.3.0: {} + + ieee754@1.2.1: {} + + iron-webcrypto@1.2.1: {} + + is-buffer@1.1.6: + optional: true + + is-docker@3.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-retry-allowed@2.2.0: + optional: true + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + isows@1.0.7(ws@8.21.0): + dependencies: + ws: 8.21.0 + + jose@6.2.6: + optional: true + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyvaluestorage-interface@1.0.0: {} + + lit-element@4.2.2: + dependencies: + '@lit-labs/ssr-dom-shim': 1.6.0 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.3 + + lit-html@3.3.3: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.0: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.3 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lossless-json@4.3.1: {} + + loupe@3.2.1: {} + + lru-cache@11.5.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: + optional: true + + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + optional: true + + micro-packed@0.7.3: + dependencies: + '@scure/base': 1.2.6 + + micro-sol-signer@0.5.0: + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + micro-packed: 0.7.3 + + mime-db@1.52.0: + optional: true + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + optional: true + + mipd@0.0.7(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + ms@2.1.3: {} + + multiformats@9.9.0: {} + + nanoid@3.3.16: {} + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build-optional-packages@5.1.1: + dependencies: + detect-libc: 2.1.2 + optional: true + + node-mock-http@1.0.5: {} + + node-releases@2.0.51: {} + + normalize-path@3.0.0: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + on-exit-leak-free@2.1.2: {} + + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + ox@0.14.33(typescript@5.9.3)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.14.33(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.4.4(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.3.0(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.6.9(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.3.0(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + optional: true + + ox@0.9.3(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.3.0(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + pako@2.2.0: {} + + path-exists@4.0.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.5: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@10.0.0: + dependencies: + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.1.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + slow-redact: 0.3.2 + sonic-boom: 4.2.1 + thread-stream: 3.2.0 + + playwright-core@1.62.1: {} + + playwright@1.62.1: + dependencies: + playwright-core: 1.62.1 + optionalDependencies: + fsevents: 2.3.2 + + pngjs@5.0.0: {} + + postcss@8.5.25: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.24.2: + optional: true + + process-warning@5.1.0: {} + + proxy-compare@3.0.1: {} + + proxy-from-env@2.1.0: + optional: true + + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + + qrcode@1.5.3: + dependencies: + dijkstrajs: 1.0.3 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + quick-format-unescaped@4.0.4: {} + + radix3@1.1.2: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-refresh@0.17.0: {} + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + readdirp@5.0.0: {} + + real-require@0.2.0: {} + + redeyed@2.1.1: + dependencies: + esprima: 4.0.1 + + reflect-metadata@0.2.2: {} + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + rollup@4.62.3: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.3 + '@rollup/rollup-android-arm64': 4.62.3 + '@rollup/rollup-darwin-arm64': 4.62.3 + '@rollup/rollup-darwin-x64': 4.62.3 + '@rollup/rollup-freebsd-arm64': 4.62.3 + '@rollup/rollup-freebsd-x64': 4.62.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.3 + '@rollup/rollup-linux-arm-musleabihf': 4.62.3 + '@rollup/rollup-linux-arm64-gnu': 4.62.3 + '@rollup/rollup-linux-arm64-musl': 4.62.3 + '@rollup/rollup-linux-loong64-gnu': 4.62.3 + '@rollup/rollup-linux-loong64-musl': 4.62.3 + '@rollup/rollup-linux-ppc64-gnu': 4.62.3 + '@rollup/rollup-linux-ppc64-musl': 4.62.3 + '@rollup/rollup-linux-riscv64-gnu': 4.62.3 + '@rollup/rollup-linux-riscv64-musl': 4.62.3 + '@rollup/rollup-linux-s390x-gnu': 4.62.3 + '@rollup/rollup-linux-x64-gnu': 4.62.3 + '@rollup/rollup-linux-x64-musl': 4.62.3 + '@rollup/rollup-openbsd-x64': 4.62.3 + '@rollup/rollup-openharmony-arm64': 4.62.3 + '@rollup/rollup-win32-arm64-msvc': 4.62.3 + '@rollup/rollup-win32-ia32-msvc': 4.62.3 + '@rollup/rollup-win32-x64-gnu': 4.62.3 + '@rollup/rollup-win32-x64-msvc': 4.62.3 + fsevents: 2.3.3 + + run-applescript@7.1.0: {} + + safe-stable-stringify@2.5.0: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.7.2: {} + + set-blocking@2.0.0: {} + + sha256-uint8array@0.10.7: {} + + siginfo@2.0.0: {} + + slow-redact@0.3.2: {} + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.1: {} + + split2@4.2.0: {} + + stackback@0.0.2: {} + + starknet@8.9.2: + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/base': 1.2.6 + '@scure/starknet': 1.1.0 + '@starknet-io/starknet-types-08': '@starknet-io/types-js@0.8.4' + '@starknet-io/starknet-types-09': '@starknet-io/types-js@0.9.2' + abi-wan-kanabi: 2.2.4 + lossless-json: 4.3.1 + pako: 2.2.0 + ts-mixer: 6.0.4 + + starknet@9.4.2(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/base': 1.2.6 + '@scure/starknet': 1.1.0 + '@starknet-io/get-starknet-wallet-standard': 5.0.0(typescript@5.9.3)(zod@3.25.76) + '@starknet-io/starknet-types-010': '@starknet-io/types-js@0.10.0' + '@starknet-io/starknet-types-09': '@starknet-io/types-js@0.9.2' + abi-wan-kanabi: 2.2.4 + lossless-json: 4.3.1 + pako: 2.2.0 + ts-mixer: 6.0.4 + transitivePeerDependencies: + - typescript + - zod + + std-env@3.10.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + thread-stream@3.2.0: + dependencies: + real-require: 0.2.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + tr46@0.0.3: {} + + ts-mixer@6.0.4: {} + + tslib@1.14.1: {} + + tslib@2.7.0: {} + + tslib@2.8.1: {} + + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + uint8arrays@3.1.1: + dependencies: + multiformats: 9.9.0 + + uncrypto@0.1.3: {} + + undici-types@6.19.8: {} + + undici-types@7.29.0: + optional: true + + universalify@2.0.1: {} + + unstorage@1.17.5(idb-keyval@6.3.0): + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.2 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + optionalDependencies: + idb-keyval: 6.3.0 + + update-browserslist-db@1.2.3(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + + valtio@2.1.7(@types/react@18.3.31)(react@18.3.1): + dependencies: + proxy-compare: 3.0.1 + optionalDependencies: + '@types/react': 18.3.31 + react: 18.3.1 + + viem@2.55.10(typescript@5.9.3)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.22.4) + isows: 1.0.7(ws@8.21.0) + ox: 0.14.33(typescript@5.9.3)(zod@3.22.4) + ws: 8.21.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.55.10(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.21.0) + ox: 0.14.33(typescript@5.9.3)(zod@3.25.76) + ws: 8.21.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + vite-node@3.2.4(@types/node@22.7.5): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.21(@types/node@22.7.5): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.25 + rollup: 4.62.3 + optionalDependencies: + '@types/node': 22.7.5 + fsevents: 2.3.3 + + vitest@3.2.7(@types/node@22.7.5): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.7 + '@vitest/mocker': 3.2.7(vite@5.4.21(@types/node@22.7.5)) + '@vitest/pretty-format': 3.2.7 + '@vitest/runner': 3.2.7 + '@vitest/snapshot': 3.2.7 + '@vitest/spy': 3.2.7 + '@vitest/utils': 3.2.7 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.4.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 5.4.21(@types/node@22.7.5) + vite-node: 3.2.4(@types/node@22.7.5) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.7.5 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-module@2.0.1: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + ws@7.5.13: {} + + ws@8.21.0: {} + + ws@8.21.1: + optional: true + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + zod@3.22.4: {} + + zod@3.25.76: {} + + zustand@5.0.3(@types/react@18.3.31)(react@18.3.1): + optionalDependencies: + '@types/react': 18.3.31 + react: 18.3.1 + optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..a40b32c --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - sdk + - app diff --git a/scripts/gen-tables.mjs b/scripts/gen-tables.mjs new file mode 100644 index 0000000..cb66163 --- /dev/null +++ b/scripts/gen-tables.mjs @@ -0,0 +1,79 @@ +#!/usr/bin/env node +// Regenerates sdk/src/tables.ts from src/beast_definitions.cairo. +// +// The name tables are the one part of the SDK that genuinely duplicates +// contract data, so they are generated rather than transcribed: a hand-copied +// list of 75 species drifts silently, and a wrong species name is a wrong NFT. +// +// Tiers and types are NOT generated — they are formulas (see sdk/src/species.ts), +// and the SDK's tests pin them against the contract's own test anchors. + +import { readFileSync, writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..'); +const source = readFileSync(join(root, 'src/beast_definitions.cairo'), 'utf8'); + +/** + * Each table is an if/else chain of short-string literals in contract order, + * with a fallback literal at the end that is not a real entry. + */ +function extractTable(fnName, expected) { + const start = source.indexOf(`pub fn ${fnName}(`); + if (start === -1) throw new Error(`could not find ${fnName}`); + + // Run to the next top-level `pub fn`, or the end of the file. + const next = source.indexOf('\npub fn ', start + 1); + const body = source.slice(start, next === -1 ? undefined : next); + + const names = [...body.matchAll(/^\s*'([^']*)'/gm)].map((m) => m[1]); + const entries = names.slice(0, expected); + + if (entries.length !== expected) { + throw new Error(`${fnName}: expected ${expected} entries, found ${entries.length}`); + } + if (entries.some((n) => n.length === 0)) { + throw new Error(`${fnName}: found an empty entry — the parse is misaligned`); + } + return entries; +} + +const names = extractTable('get_beast_name', 75); +const prefixes = extractTable('get_prefix', 69); +const suffixes = extractTable('get_suffix', 18); + +const block = (arr, perLine = 5) => { + const lines = []; + for (let i = 0; i < arr.length; i += perLine) { + lines.push(' ' + arr.slice(i, i + perLine).map((x) => `'${x}',`).join(' ')); + } + return lines.join('\n'); +}; + +const output = `// GENERATED from src/beast_definitions.cairo — do not edit by hand. +// Regenerate with \`node scripts/gen-tables.mjs\` from the repo root. +// +// Index 0 of each array is species/affix 1: the contract numbers these from +// 1, and 0 means "absent" for affixes. + +/** The 75 genesis species, in contract order (index 0 === species 1). */ +export const GENESIS_SPECIES_NAMES: readonly string[] = [ +${block(names)} +] as const; + +/** Name prefixes 1-69. Shared by every species, genesis and community. */ +export const PREFIX_NAMES: readonly string[] = [ +${block(prefixes)} +] as const; + +/** Name suffixes 1-18. Shared by every species, genesis and community. */ +export const SUFFIX_NAMES: readonly string[] = [ +${block(suffixes)} +] as const; +`; + +writeFileSync(join(root, 'sdk/src/tables.ts'), output); +console.log( + `Wrote sdk/src/tables.ts: ${names.length} species, ${prefixes.length} prefixes, ${suffixes.length} suffixes`, +); diff --git a/sdk/.gitignore b/sdk/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/sdk/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/sdk/README.md b/sdk/README.md new file mode 100644 index 0000000..3f9c4f3 --- /dev/null +++ b/sdk/README.md @@ -0,0 +1,108 @@ +# @provable-games/beasts-sdk + +TypeScript SDK for the Beasts onchain bestiary. + +```bash +pnpm add @provable-games/beasts-sdk starknet +``` + +## Everything static is offline + +A Beast's token ID *is* the Beast. The 116-bit layout encodes species, affixes, +level, health, tier, type, and the shiny/animated flags, so the full static +profile is recoverable without touching a node: + +```ts +import { decodeTokenId, beastPower, fullBeastName } from '@provable-games/beasts-sdk'; + +const beast = decodeTokenId('0x2e0064000a081000000000000004c'); +// { id: 76n, prefix: 1, suffix: 1, level: 10, health: 100, +// shiny: 0, animated: 1, tier: 3, beastType: 1 } + +beastPower(beast); // 30 — level * (6 - tier) +``` + +Only two things need a chain read, and both cache per species: the **name** and +the **art** of community species. Genesis species (1–75) resolve entirely +offline: + +```ts +import { genesisSpecies, fullBeastName, decodeTokenId } from '@provable-games/beasts-sdk'; + +genesisSpecies(1n); // { name: 'Warlock', tier: 1, beastType: 0 } +fullBeastName(decodeTokenId(genesisWarlockTokenId)); // 'Warlock' +fullBeastName(communityBeast, 'Gloomfang'); // '"Agony Bane" Gloomfang' +``` + +## Genesis Beasts are derived, not flagged + +There is no genesis bit. The `(id, 0, 0)` affix slot is reserved as each +species' Genesis Beast — the artist's provenance token — so `isGenesis` is just +`prefix === 0 && suffix === 0`. Every other mint requires both affixes, which is +what caps a species at exactly 1,243. + +## Validation mirrors the contract + +`validateSpeciesName`, `validateRenderableArt`, `validateFactoryArt` and +`validateArtSet` reproduce the contract's guards so a UI can fail fast instead +of failing a transaction. They are a convenience, never the security boundary — +the contract re-checks everything and is the only thing that decides what is +valid. + +The name charset is an injection guard, not a style rule: the contract's JSON +and SVG builders embed names unescaped. Names are deliberately **not** unique — +requiring uniqueness would let anyone squat the good ones. + +## Finding what a wallet controls + +```ts +await client.getSpeciesByArtist(address); // [77n, 78n] +await client.getOwnedSpecies(address); // + definition and Genesis token ID +``` + +**The artist role is not stored anywhere — it *is* ownership of the species' +Genesis Beast.** So this walks the wallet's tokens through the collection's +`token_of_owner_by_index`, decodes each ID locally, and keeps the ones with no +affixes. No registry reads, no event scanning: a token ID already carries its +species. + +Transferring a species is therefore an ordinary ERC721 transfer +(`transferGenesisBeastCall`), and a marketplace sale does the same thing. + +## Reads and calls + +`BeastsClient` returns `Call` objects rather than sending them, so the caller +decides how to sign — a wallet popup, a session, or a multicall batching several +changes into one transaction. + +```ts +import { BeastsClient, BeastType, SEPOLIA_ADDRESSES } from '@provable-games/beasts-sdk'; +import { RpcProvider } from 'starknet'; + +const client = new BeastsClient(new RpcProvider({ nodeUrl }), SEPOLIA_ADDRESSES, account); + +await client.execute( + client.registerWithArtCall({ + name: 'Gloomfang', + beastType: BeastType.Hunter, + tier: 3, + minter: dungeonAddress, // '0x0' registers paused + art: { pngRegular, pngShiny, gifRegular, gifShiny }, + }), +); +``` + +## Tests + +```bash +pnpm test # unit tests, no network +pnpm test:live # additionally reads the Sepolia deployment +``` + +The unit tests are anchored on two token IDs produced by the deployed contract, +not by this SDK, so a layout drift between Cairo and TypeScript fails the suite. +The live tests prove the client's calldata and ABI decoding match the deployed +contracts — something fixtures cannot. + +`src/tables.ts` is generated from `src/beast_definitions.cairo`; regenerate with +`node scripts/gen-tables.mjs` from the repo root rather than editing it. diff --git a/sdk/package.json b/sdk/package.json new file mode 100644 index 0000000..a7121d1 --- /dev/null +++ b/sdk/package.json @@ -0,0 +1,30 @@ +{ + "name": "@provable-games/beasts-sdk", + "version": "0.1.0", + "description": "TypeScript SDK for the Beasts onchain bestiary: token ID codec, species resolution, and the permissionless registry.", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "files": ["dist"], + "scripts": { + "build": "tsc -p tsconfig.build.json", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:live": "RUN_LIVE_TESTS=1 vitest run" + }, + "peerDependencies": { + "starknet": "^9.0.0" + }, + "devDependencies": { + "starknet": "9.4.2", + "typescript": "^5.8.3", + "vitest": "^3.0.0" + }, + "license": "MIT" +} diff --git a/sdk/src/index.ts b/sdk/src/index.ts new file mode 100644 index 0000000..e36d2e6 --- /dev/null +++ b/sdk/src/index.ts @@ -0,0 +1,54 @@ +export { + FIRST_COMMUNITY_ID, + GENESIS_SPECIES_MAX, + MAX_SUPPLY_PER_SPECIES, + TOKEN_ID_BITS, + TokenIdError, + beastPower, + decodeTokenId, + encodeTokenId, + genesisBeast, + isGenesis, + isGenesisSpecies, +} from './tokenId.js'; + +export { + fullBeastName, + genesisSpecies, + genesisSpeciesName, + genesisTier, + genesisType, + prefixName, + suffixName, +} from './species.js'; + +export { + ALLOWED_ART_PREFIXES, + MAX_NAME_BYTES, + type ValidationResult, + validateArtSet, + validateFactoryArt, + validateRenderableArt, + validateSpeciesName, + validateTier, +} from './validation.js'; + +export { + BeastsClient, + SEPOLIA_ADDRESSES, + type BeastsAddresses, + type OwnedSpecies, + type RegisterParams, + type RegisterWithArtParams, + type SpeciesSummary, +} from './registry.js'; + +export { GENESIS_SPECIES_NAMES, PREFIX_NAMES, SUFFIX_NAMES } from './tables.js'; + +export { + BEAST_TYPE_NAMES, + BeastType, + type ArtSet, + type Beast, + type BeastDefinition, +} from './types.js'; diff --git a/sdk/src/registry.ts b/sdk/src/registry.ts new file mode 100644 index 0000000..d7169db --- /dev/null +++ b/sdk/src/registry.ts @@ -0,0 +1,666 @@ +import type { AccountInterface, Call, ProviderInterface } from 'starknet'; +import { CallData, byteArray, shortString } from 'starknet'; +import { genesisSpecies, genesisSpeciesName } from './species.js'; +import { + FIRST_COMMUNITY_ID, + decodeTokenId, + encodeTokenId, + genesisBeast, + isGenesis, + isGenesisSpecies, +} from './tokenId.js'; +import { BeastType, type ArtSet, type Beast, type BeastDefinition } from './types.js'; + +/** Name, traits and mint count for any species, genesis or community. */ +export interface SpeciesSummary { + beastId: bigint; + name: string; + tier: number; + beastType: BeastType; + /** Non-genesis mints. The Genesis Beast is not counted. */ + minted: number; + /** False for the original 75, which have no registry entry. */ + community: boolean; +} + +/** A species the connected wallet controls, with its provenance token. */ +export interface OwnedSpecies { + beastId: bigint; + definition: BeastDefinition; + /** Token ID of the species' Genesis Beast — the artist's provenance token. */ + genesisTokenId: bigint; +} + +/** Addresses of a deployed Beasts stack. */ +export interface BeastsAddresses { + nft: string; + registry: string; +} + +/** Deployed stacks, from `docs/sepolia-v3-deployment.md`. */ +export const SEPOLIA_ADDRESSES: BeastsAddresses = { + nft: '0x017e2cb5d7c4a86ff2bdee182ce53386a7cc57c63b943878de21b681e336a89a', + registry: '0x0797a19c0b267e91ea17f886f155310e38196261ec5683e3a12a35772718d723', +}; + +export interface RegisterWithArtParams { + name: string; + beastType: BeastType; + tier: number; + /** Dungeon allowed to mint. Pass `'0x0'` to register in a paused state. */ + minter: string; + art: ArtSet; +} + +export interface RegisterParams { + name: string; + beastType: BeastType; + tier: number; + minter: string; + /** An `IBeastArtProvider` the artist controls. */ + artProvider: string; +} + +/** + * Reads and call-builders for the Beasts stack. + * + * Write methods return `Call` objects rather than executing them, so the + * caller decides how to sign — a wallet popup, a Cartridge session, or a + * multicall batching several changes into one transaction. Only + * `execute` actually sends, and only if an account was supplied. + */ +export class BeastsClient { + private legacyProviders?: { + pngRegular: string; + pngShiny: string; + gifRegular: string; + gifShiny: string; + }; + + constructor( + private readonly provider: ProviderInterface, + private readonly addresses: BeastsAddresses = SEPOLIA_ADDRESSES, + private readonly account?: AccountInterface, + ) {} + + get nftAddress(): string { + return this.addresses.nft; + } + + get registryAddress(): string { + return this.addresses.registry; + } + + // ------------------------------------------------------------- reads + + /** Full definition of a registered community species. Reverts for 1-75. */ + async getDefinition(beastId: bigint): Promise { + const raw = (await this.provider.callContract({ + contractAddress: this.addresses.registry, + entrypoint: 'get_definition', + calldata: CallData.compile([beastId.toString()]), + })) as string[]; + + // BeastDefinition: name, type, tier, minter, artist, art_provider, + // stats_source, factory, art_locked, minter_locked + return { + name: shortString.decodeShortString(raw[0]), + beastType: Number(BigInt(raw[1])) as BeastType, + tier: Number(BigInt(raw[2])), + minter: toHex(raw[3]), + artist: toHex(raw[4]), + artProvider: toHex(raw[5]), + statsSource: toHex(raw[6]), + factoryProvider: BigInt(raw[7]) === 1n, + artLocked: BigInt(raw[8]) === 1n, + minterLocked: BigInt(raw[9]) === 1n, + }; + } + + /** Total species, genesis included. `next_id - 1`. */ + async speciesCount(): Promise { + const [raw] = (await this.provider.callContract({ + contractAddress: this.addresses.registry, + entrypoint: 'species_count', + calldata: [], + })) as string[]; + return BigInt(raw); + } + + async isRegistered(beastId: bigint): Promise { + const [raw] = (await this.provider.callContract({ + contractAddress: this.addresses.registry, + entrypoint: 'is_registered', + calldata: CallData.compile([beastId.toString()]), + })) as string[]; + return BigInt(raw) === 1n; + } + + /** Minted NFT count. Not the highest token ID — IDs are not sequential. */ + async totalSupply(): Promise { + const raw = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'total_supply', + calldata: [], + })) as string[]; + return u256FromParts(raw[0], raw[1]); + } + + /** Decodes a token's Beast. Local decode; no chain read is needed. */ + decodeBeast(tokenId: bigint | string): Beast { + return decodeTokenId(tokenId); + } + + async ownerOf(tokenId: bigint): Promise { + const [raw] = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'owner_of', + calldata: CallData.compile(u256ToParts(tokenId)), + })) as string[]; + return toHex(raw); + } + + /** Rank within the species, by power then health. Genesis Beasts are 0. */ + async getBeastRank(tokenId: bigint): Promise { + const [raw] = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'get_beast_rank', + calldata: CallData.compile(u256ToParts(tokenId)), + })) as string[]; + return Number(BigInt(raw)); + } + + /** The contract's own metadata for a token: a base64 JSON data URI. */ + async tokenUri(tokenId: bigint): Promise { + const raw = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'token_uri', + calldata: CallData.compile(u256ToParts(tokenId)), + })) as string[]; + return byteArray.stringFromByteArray(decodeByteArray(raw)); + } + + /** Art for a Beast, straight from its species' provider. */ + async getArt(beast: Beast): Promise { + // Mirrors the contract's own routing in `resolve_art`: genesis species + // read from the four art data contracts wired at construction, community + // species from their registered provider. + if (isGenesisSpecies(beast.id)) { + const providers = await this.getLegacyArtProviders(); + const address = beast.animated + ? beast.shiny + ? providers.gifShiny + : providers.gifRegular + : beast.shiny + ? providers.pngShiny + : providers.pngRegular; + // The legacy interface is keyed by the u8 species ID, not the beast. + return this.callForByteArray(address, 'get_data_uri', [beast.id.toString()]); + } + + const definition = await this.getDefinition(beast.id); + return this.callForByteArray(definition.artProvider, 'get_data_uri', [ + beast.id.toString(), + beast.prefix, + beast.suffix, + beast.level, + beast.health, + beast.shiny, + beast.animated, + beast.tier, + beast.beastType, + ]); + } + + /** + * Addresses of the four genesis art data contracts, read once and cached. + * They are set at construction and immutable, so a single read is safe for + * the lifetime of the client. + */ + async getLegacyArtProviders(): Promise<{ + pngRegular: string; + pngShiny: string; + gifRegular: string; + gifShiny: string; + }> { + if (!this.legacyProviders) { + const [pngRegular, pngShiny, gifRegular, gifShiny] = await Promise.all([ + this.callForAddress('get_regular_png_provider'), + this.callForAddress('get_shiny_png_provider'), + this.callForAddress('get_regular_gif_provider'), + this.callForAddress('get_shiny_gif_provider'), + ]); + this.legacyProviders = { pngRegular, pngShiny, gifRegular, gifShiny }; + } + return this.legacyProviders; + } + + /** Display name of any species: baked-in tables below 76, registry above. */ + async getSpeciesName(beastId: bigint): Promise { + if (isGenesisSpecies(beastId)) return genesisSpeciesName(beastId); + return (await this.getDefinition(beastId)).name; + } + + /** + * Name, traits and mint count for any species, genesis or community. + * + * Genesis species resolve entirely offline except the count, which is the + * one thing only the chain knows. + */ + async getSpeciesSummary(beastId: bigint): Promise { + const minted = await this.getSpeciesMintCount(beastId); + + if (isGenesisSpecies(beastId)) { + const { name, tier, beastType } = genesisSpecies(beastId); + return { beastId, name, tier, beastType, minted, community: false }; + } + + const definition = await this.getDefinition(beastId); + return { + beastId, + name: definition.name, + tier: definition.tier, + beastType: definition.beastType, + minted, + community: true, + }; + } + + /** Non-genesis mints of a species. Excludes the rank-0 Genesis Beast. */ + async getSpeciesMintCount(beastId: bigint): Promise { + const [raw] = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'get_species_count', + calldata: CallData.compile([beastId.toString()]), + })) as string[]; + return Number(BigInt(raw)); + } + + /** + * Every token of a species, best rank first, with the Genesis Beast last. + * + * Uses the contract's own per-species rank list rather than scanning: the + * NFT already keeps `rank -> token_id` for each species to drive metadata + * refreshes. + */ + async getSpeciesTokens(beastId: bigint, concurrency = 4): Promise { + const count = await this.getSpeciesMintCount(beastId); + const tokens: bigint[] = []; + + for (let start = 1; start <= count; start += concurrency) { + const batch = await Promise.all( + Array.from({ length: Math.min(concurrency, count - start + 1) }, (_, offset) => + this.getTokenIdAtRank(beastId, start + offset), + ), + ); + tokens.push(...batch.filter((id) => id !== 0n)); + } + + // The Genesis Beast holds rank 0 and lives outside that list. + const genesis = await this.getGenesisTokenIdIfMinted(beastId); + if (genesis !== null) tokens.push(genesis); + + return tokens; + } + + async getTokenIdAtRank(beastId: bigint, rank: number): Promise { + const raw = (await withRateLimitRetry( + () => + this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'get_token_id_at_rank', + calldata: CallData.compile([beastId.toString(), rank]), + }) as Promise, + )); + return u256FromParts(raw[0], raw[1]); + } + + /** Every token an address holds, decoded. */ + async getTokensOfOwner(owner: string, concurrency = 4): Promise { + const balance = Number(await this.balanceOf(owner)); + const beasts: Beast[] = []; + + for (let start = 0; start < balance; start += concurrency) { + const batch = await Promise.all( + Array.from({ length: Math.min(concurrency, balance - start) }, (_, offset) => + this.tokenOfOwnerByIndex(owner, BigInt(start + offset)), + ), + ); + beasts.push(...batch.map((id) => decodeTokenId(id))); + } + + return beasts; + } + + private async getGenesisTokenIdIfMinted(beastId: bigint): Promise { + const tokenId = isGenesisSpecies(beastId) + ? encodeTokenId( + genesisBeast(beastId, genesisSpecies(beastId).tier, genesisSpecies(beastId).beastType), + ) + : await this.getGenesisTokenId(beastId); + try { + await this.ownerOf(tokenId); + return tokenId; + } catch { + return null; + } + } + + private async callForAddress(entrypoint: string): Promise { + const [raw] = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint, + calldata: [], + })) as string[]; + return toHex(raw); + } + + private async callForByteArray( + contractAddress: string, + entrypoint: string, + calldata: unknown[], + ): Promise { + const raw = (await withRateLimitRetry( + () => + this.provider.callContract({ + contractAddress, + entrypoint, + calldata: CallData.compile(calldata as never), + }) as Promise, + )); + return byteArray.stringFromByteArray(decodeByteArray(raw)); + } + + // ------------------------------------------------- artist enumeration + + /** + * Species IDs an address controls. + * + * The artist role is not stored anywhere — it *is* ownership of the + * species' Genesis Beast. So this walks the wallet's tokens through + * `token_of_owner_by_index`, decodes each one locally, and keeps the ones + * with no affixes. A token ID carries its own species, so no registry read + * and no event scan is involved. + */ + async getSpeciesByArtist(artist: string, concurrency = 4): Promise { + const balance = Number(await this.balanceOf(artist)); + const ids: bigint[] = []; + + // Enumeration is one call per token, and the collection owner alone holds + // all 75 genesis Beasts — issuing those serially takes tens of seconds and + // trips public-node rate limits. Fetch in bounded batches instead: + // fast enough for a real wallet, still polite to the node. + for (let start = 0; start < balance; start += concurrency) { + const batch = await Promise.all( + Array.from({ length: Math.min(concurrency, balance - start) }, (_, offset) => + this.tokenOfOwnerByIndex(artist, BigInt(start + offset)), + ), + ); + for (const tokenId of batch) { + const beast = decodeTokenId(tokenId); + // The (id, 0, 0) slot is the species' Genesis Beast, and holding it is + // what the registry checks. Every other token is an ordinary Beast. + if (isGenesis(beast)) ids.push(beast.id); + } + } + + return ids.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); + } + + /** + * Full detail for everything an address controls, ready to render. + * + * Genesis species (1-75) are skipped: their Genesis Beasts belong to the + * collection owner and have no registry entry, so `get_definition` would + * revert on them. + */ + async getOwnedSpecies(artist: string): Promise { + const ids = (await this.getSpeciesByArtist(artist)).filter( + (id) => id >= FIRST_COMMUNITY_ID, + ); + return Promise.all( + ids.map(async (beastId) => { + const definition = await this.getDefinition(beastId); + return { + beastId, + definition, + genesisTokenId: encodeTokenId( + genesisBeast(beastId, definition.tier, definition.beastType), + ), + }; + }), + ); + } + + /** + * Token ID of the species' Genesis Beast, read from the registry. + * + * Derivable offline via `encodeTokenId(genesisBeast(...))`, but reading it + * back is what proves the client and the contract agree on which token + * carries the artist role. + */ + async getGenesisTokenId(beastId: bigint): Promise { + const raw = (await this.provider.callContract({ + contractAddress: this.addresses.registry, + entrypoint: 'get_genesis_token_id', + calldata: CallData.compile([beastId.toString()]), + })) as string[]; + return u256FromParts(raw[0], raw[1]); + } + + async balanceOf(owner: string): Promise { + const raw = (await this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'balance_of', + calldata: CallData.compile([owner]), + })) as string[]; + return u256FromParts(raw[0], raw[1]); + } + + /** Owner enumeration. Order is not stable across transfers. */ + async tokenOfOwnerByIndex(owner: string, index: bigint): Promise { + const raw = (await withRateLimitRetry( + () => + this.provider.callContract({ + contractAddress: this.addresses.nft, + entrypoint: 'token_of_owner_by_index', + calldata: CallData.compile([owner, ...u256ToParts(index)]), + }) as Promise, + )); + return u256FromParts(raw[0], raw[1]); + } + + // ------------------------------------------------------ call builders + + /** + * The simple path: the registry deploys a canonical `StoredArtProvider` + * holding the four variants. One transaction, no contract knowledge needed, + * and the artist's Genesis Beast lands in the same transaction. + */ + registerWithArtCall(params: RegisterWithArtParams): Call { + return { + contractAddress: this.addresses.registry, + entrypoint: 'register_beast_with_art', + calldata: CallData.compile([ + shortString.encodeShortString(params.name), + params.beastType, + params.tier, + params.minter, + byteArray.byteArrayFromString(params.art.pngRegular), + byteArray.byteArrayFromString(params.art.pngShiny), + byteArray.byteArrayFromString(params.art.gifRegular), + byteArray.byteArrayFromString(params.art.gifShiny), + ]), + }; + } + + /** The advanced path: the artist supplies their own `IBeastArtProvider`. */ + registerCall(params: RegisterParams): Call { + return { + contractAddress: this.addresses.registry, + entrypoint: 'register_beast', + calldata: CallData.compile([ + shortString.encodeShortString(params.name), + params.beastType, + params.tier, + params.minter, + params.artProvider, + ]), + }; + } + + updateArtCall(beastId: bigint, art: ArtSet): Call { + return { + contractAddress: this.addresses.registry, + entrypoint: 'update_art', + calldata: CallData.compile([ + beastId.toString(), + byteArray.byteArrayFromString(art.pngRegular), + byteArray.byteArrayFromString(art.pngShiny), + byteArray.byteArrayFromString(art.gifRegular), + byteArray.byteArrayFromString(art.gifShiny), + ]), + }; + } + + /** Zero pauses the species — mints revert until a minter is set again. */ + setMinterCall(beastId: bigint, minter: string): Call { + return this.registryCall('set_minter', [beastId.toString(), minter]); + } + + /** One-way. After this the minter can never change. */ + lockMinterCall(beastId: bigint): Call { + return this.registryCall('lock_minter', [beastId.toString()]); + } + + /** + * One-way. Freezes the art *pointer*. For a factory provider that is a true + * freeze, since its only mutator is registry-gated. For a custom provider it + * freezes only the address — the provider may still change what it returns, + * which is why `notify_art_updated` stays open for those. + */ + lockArtCall(beastId: bigint): Call { + return this.registryCall('lock_art', [beastId.toString()]); + } + + setArtProviderCall(beastId: bigint, provider: string): Call { + return this.registryCall('set_art_provider', [beastId.toString(), provider]); + } + + /** Re-announces art to marketplaces after a custom provider changed. */ + notifyArtUpdatedCall(beastId: bigint): Call { + return this.registryCall('notify_art_updated', [beastId.toString()]); + } + + /** Zero clears the source, turning kill stats off for the species. */ + setStatsSourceCall(beastId: bigint, source: string): Call { + return this.registryCall('set_stats_source', [beastId.toString(), source]); + } + + /** + * Transfers a species by transferring its Genesis Beast. + * + * There is no `transfer_artist_role`: the creator token *is* the role, so + * this is an ordinary ERC721 transfer and a marketplace sale does exactly + * the same thing. + */ + transferGenesisBeastCall(from: string, to: string, genesisTokenId: bigint): Call { + return { + contractAddress: this.addresses.nft, + entrypoint: 'transfer_from', + calldata: CallData.compile([from, to, ...u256ToParts(genesisTokenId)]), + }; + } + + /** Permissionless: pulls a community token's stats into the render cache. */ + refreshStatsCall(tokenId: bigint): Call { + return { + contractAddress: this.addresses.nft, + entrypoint: 'refresh_stats', + calldata: CallData.compile(u256ToParts(tokenId)), + }; + } + + // ----------------------------------------------------------- execute + + /** Sends one or more calls. Requires an account. */ + async execute(calls: Call | Call[]): Promise { + if (!this.account) { + throw new Error('BeastsClient was constructed without an account; cannot execute'); + } + const { transaction_hash } = await this.account.execute(calls); + return transaction_hash; + } + + private registryCall(entrypoint: string, args: unknown[]): Call { + return { + contractAddress: this.addresses.registry, + entrypoint, + calldata: CallData.compile(args as never), + }; + } +} + +// ------------------------------------------------------------- helpers + +/** + * Retries a call that a public node refused for rate limiting. + * + * Enumeration is one request per token, so any wallet with a real collection + * will out-run a free endpoint's per-second budget. That is a transient + * refusal, not an answer — retrying with backoff is the difference between + * "this wallet holds nothing" and the truth. + */ +async function withRateLimitRetry(fn: () => Promise, attempts = 7): Promise { + let delayMs = 250; + for (let attempt = 0; ; attempt++) { + try { + return await fn(); + } catch (error) { + if (attempt >= attempts - 1 || !isRateLimited(error)) throw error; + await new Promise((resolve) => setTimeout(resolve, delayMs)); + delayMs *= 2; + } + } +} + +function isRateLimited(error: unknown): boolean { + const message = (error instanceof Error ? error.message : String(error)).toLowerCase(); + return ( + message.includes('-32011') || + message.includes('cu limit') || + message.includes('too many requests') || + message.includes('rate limit') || + message.includes('429') + ); +} + +function toHex(value: string): string { + return `0x${BigInt(value).toString(16).padStart(64, '0')}`; +} + +function u256ToParts(value: bigint): [string, string] { + const MASK = (1n << 128n) - 1n; + return [(value & MASK).toString(), (value >> 128n).toString()]; +} + +function u256FromParts(low: string, high: string): bigint { + return BigInt(low) + (BigInt(high) << 128n); +} + +/** + * A ByteArray on the wire is `[num_full_words, ...words, pending_word, + * pending_len]`. starknet.js wants that shape as an object. + */ +function decodeByteArray(raw: string[]): { + data: string[]; + pending_word: string; + pending_word_len: number; +} { + const wordCount = Number(BigInt(raw[0])); + return { + data: raw.slice(1, 1 + wordCount), + pending_word: raw[1 + wordCount], + pending_word_len: Number(BigInt(raw[2 + wordCount])), + }; +} diff --git a/sdk/src/species.ts b/sdk/src/species.ts new file mode 100644 index 0000000..416624a --- /dev/null +++ b/sdk/src/species.ts @@ -0,0 +1,75 @@ +import { GENESIS_SPECIES_NAMES, PREFIX_NAMES, SUFFIX_NAMES } from './tables.js'; +import { GENESIS_SPECIES_MAX, isGenesisSpecies } from './tokenId.js'; +import { BeastType, type Beast } from './types.js'; + +/** + * Tier of a genesis species. + * + * The contract expresses this as ranges, but they are a formula: species are + * laid out in three blocks of 25 (one per type), and each block runs T1..T5 in + * groups of five. + */ +export function genesisTier(id: bigint): number { + assertGenesisSpecies(id); + return Math.floor((Number(id - 1n) % 25) / 5) + 1; +} + +/** Type of a genesis species: 1-25 Magic, 26-50 Hunter, 51-75 Brute. */ +export function genesisType(id: bigint): BeastType { + assertGenesisSpecies(id); + return Math.floor(Number(id - 1n) / 25) as BeastType; +} + +/** Display name of a genesis species. */ +export function genesisSpeciesName(id: bigint): string { + assertGenesisSpecies(id); + return GENESIS_SPECIES_NAMES[Number(id) - 1]; +} + +/** Prefix display name, or `null` for the Genesis Beast's absent affix. */ +export function prefixName(prefix: number): string | null { + if (prefix === 0) return null; + if (prefix < 0 || prefix > PREFIX_NAMES.length) { + throw new RangeError(`prefix out of range: ${prefix}`); + } + return PREFIX_NAMES[prefix - 1]; +} + +/** Suffix display name, or `null` for the Genesis Beast's absent affix. */ +export function suffixName(suffix: number): string | null { + if (suffix === 0) return null; + if (suffix < 0 || suffix > SUFFIX_NAMES.length) { + throw new RangeError(`suffix out of range: ${suffix}`); + } + return SUFFIX_NAMES[suffix - 1]; +} + +/** + * Full display name, matching the contract's metadata exactly. + * + * A Genesis Beast is just the species name; every other Beast is + * `"Prefix Suffix" Species`. Community species must supply `speciesName` — + * only the registry knows it. + */ +export function fullBeastName(beast: Beast, speciesName?: string): string { + const base = speciesName ?? genesisSpeciesName(beast.id); + if (beast.prefix === 0) return base; + return `"${prefixName(beast.prefix)} ${suffixName(beast.suffix)}" ${base}`; +} + +/** + * Static traits of a genesis species, resolved offline. Community species get + * theirs from the registry — see `BeastRegistryClient.getDefinition`. + */ +export function genesisSpecies(id: bigint): { name: string; tier: number; beastType: BeastType } { + return { name: genesisSpeciesName(id), tier: genesisTier(id), beastType: genesisType(id) }; +} + +function assertGenesisSpecies(id: bigint): void { + if (!isGenesisSpecies(id)) { + throw new RangeError( + `species ${id} is not a genesis species (1-${GENESIS_SPECIES_MAX}); ` + + 'community species resolve through the registry', + ); + } +} diff --git a/sdk/src/tables.ts b/sdk/src/tables.ts new file mode 100644 index 0000000..2487985 --- /dev/null +++ b/sdk/src/tables.ts @@ -0,0 +1,50 @@ +// GENERATED from src/beast_definitions.cairo — do not edit by hand. +// Regenerate with `node scripts/gen-tables.mjs` from the repo root. +// +// Index 0 of each array is species/affix 1: the contract numbers these from +// 1, and 0 means "absent" for affixes. + +/** The 75 genesis species, in contract order (index 0 === species 1). */ +export const GENESIS_SPECIES_NAMES: readonly string[] = [ + 'Warlock', 'Typhon', 'Jiangshi', 'Anansi', 'Basilisk', + 'Gorgon', 'Kitsune', 'Lich', 'Chimera', 'Wendigo', + 'Rakshasa', 'Werewolf', 'Banshee', 'Draugr', 'Vampire', + 'Goblin', 'Ghoul', 'Wraith', 'Sprite', 'Kappa', + 'Fairy', 'Leprechaun', 'Kelpie', 'Pixie', 'Gnome', + 'Griffin', 'Manticore', 'Phoenix', 'Dragon', 'Minotaur', + 'Qilin', 'Ammit', 'Nue', 'Skinwalker', 'Chupacabra', + 'Weretiger', 'Wyvern', 'Roc', 'Harpy', 'Pegasus', + 'Hippogriff', 'Fenrir', 'Jaguar', 'Satori', 'Direwolf', + 'Bear', 'Wolf', 'Mantis', 'Spider', 'Rat', + 'Kraken', 'Colossus', 'Balrog', 'Leviathan', 'Tarrasque', + 'Titan', 'Nephilim', 'Behemoth', 'Hydra', 'Juggernaut', + 'Oni', 'Jotunn', 'Ettin', 'Cyclops', 'Giant', + 'Nemean Lion', 'Berserker', 'Yeti', 'Golem', 'Ent', + 'Troll', 'Bigfoot', 'Ogre', 'Orc', 'Skeleton', +] as const; + +/** Name prefixes 1-69. Shared by every species, genesis and community. */ +export const PREFIX_NAMES: readonly string[] = [ + 'Agony', 'Apocalypse', 'Armageddon', 'Beast', 'Behemoth', + 'Blight', 'Blood', 'Bramble', 'Brimstone', 'Brood', + 'Carrion', 'Cataclysm', 'Chimeric', 'Corpse', 'Corruption', + 'Damnation', 'Death', 'Demon', 'Dire', 'Dragon', + 'Dread', 'Doom', 'Dusk', 'Eagle', 'Empyrean', + 'Fate', 'Foe', 'Gale', 'Ghoul', 'Gloom', + 'Glyph', 'Golem', 'Grim', 'Hate', 'Havoc', + 'Honour', 'Horror', 'Hypnotic', 'Kraken', 'Loath', + 'Maelstrom', 'Mind', 'Miracle', 'Morbid', 'Oblivion', + 'Onslaught', 'Pain', 'Pandemonium', 'Phoenix', 'Plague', + 'Rage', 'Rapture', 'Rune', 'Skull', 'Sol', + 'Soul', 'Sorrow', 'Spirit', 'Storm', 'Tempest', + 'Torment', 'Vengeance', 'Victory', 'Viper', 'Vortex', + 'Woe', 'Wrath', 'Lights', 'Shimmering', +] as const; + +/** Name suffixes 1-18. Shared by every species, genesis and community. */ +export const SUFFIX_NAMES: readonly string[] = [ + 'Bane', 'Root', 'Bite', 'Song', 'Roar', + 'Grasp', 'Instrument', 'Glow', 'Bender', 'Shadow', + 'Whisper', 'Shout', 'Growl', 'Tear', 'Peak', + 'Form', 'Sun', 'Moon', +] as const; diff --git a/sdk/src/tokenId.ts b/sdk/src/tokenId.ts new file mode 100644 index 0000000..1dc8d0e --- /dev/null +++ b/sdk/src/tokenId.ts @@ -0,0 +1,185 @@ +import { BeastType, type Beast } from './types.js'; + +/** + * The 116-bit token ID layout, mirroring `pack_to_u256` in `src/pack.cairo`. + * + * ``` + * bits 0- 63 id (u64) + * bits 64- 70 prefix (7) + * bits 71- 75 suffix (5) + * bits 76- 91 level (16) + * bits 92-107 health (16) + * bit 108 shiny (1) + * bit 109 animated (1) + * bits 110-112 tier (3) + * bits 113-115 type (3) + * ``` + * + * Token IDs are deterministic, not sequential: the ID *is* the Beast. Every + * static trait is recoverable offline, so a client needs chain reads only for + * the species name and art of community species. + */ +const SHIFT = { + id: 0n, + prefix: 64n, + suffix: 71n, + level: 76n, + health: 92n, + shiny: 108n, + animated: 109n, + tier: 110n, + beastType: 113n, +} as const; + +const WIDTH = { + id: 64n, + prefix: 7n, + suffix: 5n, + level: 16n, + health: 16n, + shiny: 1n, + animated: 1n, + tier: 3n, + beastType: 3n, +} as const; + +const mask = (bits: bigint) => (1n << bits) - 1n; + +/** Total width of the layout. Anything above this bit must be zero. */ +export const TOKEN_ID_BITS = 116n; + +/** Highest species ID backed by the baked-in genesis tables. */ +export const GENESIS_SPECIES_MAX = 75n; + +/** First species ID the registry will assign. */ +export const FIRST_COMMUNITY_ID = 76n; + +/** Max mintable Beasts per species: 69 x 18 named variants + 1 Genesis. */ +export const MAX_SUPPLY_PER_SPECIES = 69 * 18 + 1; + +export class TokenIdError extends Error { + constructor(message: string) { + super(message); + this.name = 'TokenIdError'; + } +} + +/** + * Encodes a Beast into its canonical token ID. + * + * Validates the same ranges the contract does, so an ID produced here can + * never decode into a different Beast than the one passed in. + */ +export function encodeTokenId(beast: Beast): bigint { + assertEncodable(beast); + + return ( + (beast.id << SHIFT.id) | + (BigInt(beast.prefix) << SHIFT.prefix) | + (BigInt(beast.suffix) << SHIFT.suffix) | + (BigInt(beast.level) << SHIFT.level) | + (BigInt(beast.health) << SHIFT.health) | + (BigInt(beast.shiny) << SHIFT.shiny) | + (BigInt(beast.animated) << SHIFT.animated) | + (BigInt(beast.tier) << SHIFT.tier) | + (BigInt(beast.beastType) << SHIFT.beastType) + ); +} + +/** + * Decodes a token ID into its Beast. + * + * Applies the contract's `decode_token_id` checks: no residual high bits, a + * non-zero species, in-range tier/type/affixes, and the affix-pair rule that + * `(prefix === 0) === (suffix === 0)` — the `(id, 0, 0)` slot is reserved for + * the species' Genesis Beast and no other combination may be half-set. + */ +export function decodeTokenId(tokenId: bigint | string | number): Beast { + const packed = BigInt(tokenId); + + if (packed < 0n) throw new TokenIdError('token ID must be non-negative'); + if (packed >> TOKEN_ID_BITS) throw new TokenIdError('token ID has residual high bits'); + + const field = (key: keyof typeof SHIFT) => (packed >> SHIFT[key]) & mask(WIDTH[key]); + + const id = field('id'); + if (id === 0n) throw new TokenIdError('species ID must not be zero'); + + const prefix = Number(field('prefix')); + const suffix = Number(field('suffix')); + const tier = Number(field('tier')); + const beastType = Number(field('beastType')); + + if (tier < 1 || tier > 5) throw new TokenIdError(`tier out of range: ${tier}`); + if (beastType > 2) throw new TokenIdError(`beast type out of range: ${beastType}`); + if (prefix > 69) throw new TokenIdError(`prefix out of range: ${prefix}`); + if (suffix > 18) throw new TokenIdError(`suffix out of range: ${suffix}`); + if ((prefix === 0) !== (suffix === 0)) { + throw new TokenIdError('invalid affix combo: prefix and suffix must both be zero, or neither'); + } + + return { + id, + prefix, + suffix, + level: Number(field('level')), + health: Number(field('health')), + shiny: Number(field('shiny')) as 0 | 1, + animated: Number(field('animated')) as 0 | 1, + tier, + beastType: beastType as BeastType, + }; +} + +/** + * A Genesis Beast is derived, never stored: it is the one Beast per species + * holding the reserved `(id, 0, 0)` affix slot. It is the artist's provenance + * token for community species, and belongs to the collection owner for the + * original 75. + */ +export function isGenesis(beast: Beast): boolean { + return beast.prefix === 0 && beast.suffix === 0; +} + +/** True for the 75 species baked into the contract's tables. */ +export function isGenesisSpecies(id: bigint): boolean { + return id >= 1n && id <= GENESIS_SPECIES_MAX; +} + +/** Combat power, exactly as the contract computes it: `level * (6 - tier)`. */ +export function beastPower(beast: Beast): number { + const power = beast.level * (6 - beast.tier); + return power > 65535 ? 65535 : power; +} + +/** Builds the Genesis Beast of a species from its static traits. */ +export function genesisBeast(id: bigint, tier: number, beastType: BeastType): Beast { + return { id, prefix: 0, suffix: 0, level: 1, health: 100, shiny: 1, animated: 1, tier, beastType }; +} + +function assertEncodable(beast: Beast): void { + const inRange = (name: string, value: number | bigint, max: bigint) => { + const v = BigInt(value); + if (v < 0n || v > max) throw new TokenIdError(`${name} out of range: ${value}`); + }; + + if (beast.id <= 0n) throw new TokenIdError('species ID must be positive'); + inRange('id', beast.id, mask(WIDTH.id)); + inRange('level', beast.level, mask(WIDTH.level)); + inRange('health', beast.health, mask(WIDTH.health)); + + if (beast.prefix > 69) throw new TokenIdError(`prefix out of range: ${beast.prefix}`); + if (beast.suffix > 18) throw new TokenIdError(`suffix out of range: ${beast.suffix}`); + if (beast.prefix < 0 || beast.suffix < 0) throw new TokenIdError('affixes must be non-negative'); + if ((beast.prefix === 0) !== (beast.suffix === 0)) { + throw new TokenIdError('invalid affix combo: prefix and suffix must both be zero, or neither'); + } + if (beast.tier < 1 || beast.tier > 5) throw new TokenIdError(`tier out of range: ${beast.tier}`); + if (beast.beastType < 0 || beast.beastType > 2) { + throw new TokenIdError(`beast type out of range: ${beast.beastType}`); + } + if (beast.shiny !== 0 && beast.shiny !== 1) throw new TokenIdError('shiny must be 0 or 1'); + if (beast.animated !== 0 && beast.animated !== 1) { + throw new TokenIdError('animated must be 0 or 1'); + } +} diff --git a/sdk/src/types.ts b/sdk/src/types.ts new file mode 100644 index 0000000..38385da --- /dev/null +++ b/sdk/src/types.ts @@ -0,0 +1,59 @@ +/** Beast type codes exactly as encoded in the token ID. */ +export enum BeastType { + Magic = 0, + Hunter = 1, + Brute = 2, +} + +export const BEAST_TYPE_NAMES: Record = { + [BeastType.Magic]: 'Magic', + [BeastType.Hunter]: 'Hunter', + [BeastType.Brute]: 'Brute', +}; + +/** + * Everything a token ID encodes. This is the whole static profile of a Beast — + * no chain read is needed to produce it, which is the point of the 116-bit + * layout. + */ +export interface Beast { + /** Species ID. 1-75 are genesis species; 76+ are community species. */ + id: bigint; + /** 0-69. Zero only on a Genesis Beast. */ + prefix: number; + /** 0-18. Zero only on a Genesis Beast. */ + suffix: number; + level: number; + health: number; + shiny: 0 | 1; + animated: 0 | 1; + /** 1-5. */ + tier: number; + beastType: BeastType; +} + +/** A registered community species, as returned by `get_definition`. */ +export interface BeastDefinition { + name: string; + beastType: BeastType; + tier: number; + /** Dungeon allowed to mint this species. Zero means paused. */ + minter: string; + /** Registrant; holds the per-species admin role. */ + artist: string; + artProvider: string; + /** Zero means kill stats are off for this species. */ + statsSource: string; + /** True when `artProvider` is the registry's canonical factory deploy. */ + factoryProvider: boolean; + artLocked: boolean; + minterLocked: boolean; +} + +/** The four art variants a factory-provider species stores. */ +export interface ArtSet { + pngRegular: string; + pngShiny: string; + gifRegular: string; + gifShiny: string; +} diff --git a/sdk/src/validation.ts b/sdk/src/validation.ts new file mode 100644 index 0000000..650d014 --- /dev/null +++ b/sdk/src/validation.ts @@ -0,0 +1,151 @@ +/** + * Client-side mirrors of the contract's guards. + * + * These exist so the web app can reject bad input before it costs a + * transaction — they are a convenience, never the security boundary. The + * contract re-checks everything, and it is the only thing that decides what + * is valid. + */ + +// ---------------------------------------------------------------- names + +/** + * Species name charset, mirroring `assert_valid_name` in + * `src/beast_registry.cairo`. + * + * This is an injection guard, not a style rule: the contract's JSON and SVG + * builders embed names unescaped, so the charset is what keeps every token's + * metadata well-formed. Uniqueness is deliberately NOT enforced — requiring + * it would let anyone squat the good names. Species ID is the identity. + */ +const NAME_CHARSET = /^[A-Za-z0-9 '-]+$/; + +/** Names are stored in a single felt252, which holds at most 31 bytes. */ +export const MAX_NAME_BYTES = 31; + +export interface ValidationResult { + valid: boolean; + error?: string; +} + +export function validateSpeciesName(name: string): ValidationResult { + if (name.length === 0) return { valid: false, error: 'Name cannot be empty' }; + if (new TextEncoder().encode(name).length > MAX_NAME_BYTES) { + return { valid: false, error: `Name cannot exceed ${MAX_NAME_BYTES} bytes` }; + } + if (!NAME_CHARSET.test(name)) { + return { + valid: false, + error: "Name may only contain letters, numbers, spaces, apostrophes, and hyphens", + }; + } + if (name.startsWith(' ')) return { valid: false, error: 'Name cannot start with a space' }; + if (name.endsWith(' ')) return { valid: false, error: 'Name cannot end with a space' }; + return { valid: true }; +} + +// ------------------------------------------------------------------ art + +/** + * Media types the contract accepts from a community art provider at render + * time (`src/art_validation.cairo`). + */ +export const ALLOWED_ART_PREFIXES = [ + 'data:image/png;base64,', + 'data:image/gif;base64,', + 'data:image/webp;base64,', + 'data:image/svg+xml;base64,', +] as const; + +/** + * Base64-encoded magic bytes the *factory* provider additionally requires at + * write time (`src/stored_art_provider.cairo`). A fixed leading signature + * always encodes to a fixed character prefix, so this needs no decoding: + * PNG 89 50 4E 47 0D 0A 1A 0A + IHDR length -> "iVBORw0KGgo" + * GIF "GIF87a" -> "R0lGODdh", "GIF89a" -> "R0lGODlh" + */ +const PNG_MAGIC = 'iVBORw0KGgo'; +const GIF_MAGICS = ['R0lGODdh', 'R0lGODlh'] as const; + +const BASE64_BODY = /^[A-Za-z0-9+/]*={0,2}$/; + +/** + * Validates a data URI the way `token_uri` will when it renders: allowlisted + * media type plus a structurally sound standard-base64 payload. + * + * Deliberately imposes no size cap. If an artist is willing to pay for the + * storage and the network accepts the transaction, the art is valid. + */ +export function validateRenderableArt(uri: string): ValidationResult { + const prefix = ALLOWED_ART_PREFIXES.find((p) => uri.startsWith(p)); + if (!prefix) { + return { + valid: false, + error: 'Art must be a base64 data URI of type png, gif, webp, or svg+xml', + }; + } + + const payload = uri.slice(prefix.length); + if (payload.length === 0) return { valid: false, error: 'Art payload is empty' }; + if (payload.length % 4 !== 0) { + return { valid: false, error: 'Art payload is not valid base64 (length must be a multiple of 4)' }; + } + if (!BASE64_BODY.test(payload)) { + return { valid: false, error: 'Art payload contains characters outside the base64 alphabet' }; + } + return { valid: true }; +} + +/** + * Validates art destined for the *factory* provider, which is stricter than + * render-time: it must additionally carry real PNG or GIF magic bytes. This + * is what earns factory art its "verified" designation — a locked factory + * species is provably frozen and provably an inert image. + */ +export function validateFactoryArt(uri: string, kind: 'png' | 'gif'): ValidationResult { + const expectedPrefix = kind === 'png' ? 'data:image/png;base64,' : 'data:image/gif;base64,'; + if (!uri.startsWith(expectedPrefix)) { + return { valid: false, error: `Expected a ${kind.toUpperCase()} data URI` }; + } + + const structural = validateRenderableArt(uri); + if (!structural.valid) return structural; + + const payload = uri.slice(expectedPrefix.length); + if (kind === 'png') { + if (!payload.startsWith(PNG_MAGIC)) { + return { valid: false, error: 'File is not a valid PNG' }; + } + } else if (!GIF_MAGICS.some((m) => payload.startsWith(m))) { + return { valid: false, error: 'File is not a valid GIF (must be GIF87a or GIF89a)' }; + } + return { valid: true }; +} + +/** Validates the complete four-variant set the factory path requires. */ +export function validateArtSet(art: { + pngRegular: string; + pngShiny: string; + gifRegular: string; + gifShiny: string; +}): ValidationResult { + const checks: Array<[string, ValidationResult]> = [ + ['Regular PNG', validateFactoryArt(art.pngRegular, 'png')], + ['Shiny PNG', validateFactoryArt(art.pngShiny, 'png')], + ['Regular GIF', validateFactoryArt(art.gifRegular, 'gif')], + ['Shiny GIF', validateFactoryArt(art.gifShiny, 'gif')], + ]; + for (const [label, result] of checks) { + if (!result.valid) return { valid: false, error: `${label}: ${result.error}` }; + } + return { valid: true }; +} + +// ----------------------------------------------------------------- tier + +export function validateTier(tier: number): ValidationResult { + if (!Number.isInteger(tier) || tier < 1 || tier > 5) { + return { valid: false, error: 'Tier must be a whole number from 1 to 5' }; + } + return { valid: true }; +} diff --git a/sdk/test/live.test.ts b/sdk/test/live.test.ts new file mode 100644 index 0000000..07f2ca6 --- /dev/null +++ b/sdk/test/live.test.ts @@ -0,0 +1,124 @@ +import { RpcProvider } from 'starknet'; +import { describe, expect, it } from 'vitest'; +import { BeastType, BeastsClient, SEPOLIA_ADDRESSES, decodeTokenId } from '../src/index.js'; + +/** + * Live reads against the Sepolia deployment in + * `docs/sepolia-v3-deployment.md`. + * + * Skipped by default — they need network and will drift if that deployment is + * replaced. Run with `pnpm test:live`. Their job is to prove the client's + * calldata and return-value decoding actually match the deployed ABI, which + * unit tests with hand-written fixtures cannot. + */ +const RPC_URL = process.env.STARKNET_RPC_URL ?? 'https://api.zan.top/public/starknet-sepolia/rpc/v0_10'; +const live = process.env.RUN_LIVE_TESTS ? describe : describe.skip; + +const GENESIS_WARLOCK = 0x7006400010000000000000000001n; +const GLOOMFANG = 0x2e0064000a081000000000000004cn; // minted below if absent + +live('Sepolia deployment', () => { + const client = new BeastsClient(new RpcProvider({ nodeUrl: RPC_URL }), SEPOLIA_ADDRESSES); + + it('reads the registered community species', async () => { + const definition = await client.getDefinition(76n); + expect(definition.name).toBe('Gloomfang'); + expect(definition.beastType).toBe(BeastType.Hunter); + expect(definition.tier).toBe(3); + expect(definition.factoryProvider).toBe(true); + expect(definition.artLocked).toBe(false); + expect(definition.minterLocked).toBe(false); + // No stats source was set, so kill stats are off for this species. + expect(BigInt(definition.statsSource)).toBe(0n); + }, 30_000); + + it('counts species and supply', async () => { + expect(await client.speciesCount()).toBeGreaterThanOrEqual(76n); + expect(await client.totalSupply()).toBeGreaterThanOrEqual(76n); + }, 30_000); + + it('reports registration only above the genesis range', async () => { + expect(await client.isRegistered(76n)).toBe(true); + expect(await client.isRegistered(1n)).toBe(false); + expect(await client.isRegistered(9_999n)).toBe(false); + }, 30_000); + + it('ranks the community mint first in its species', async () => { + expect(await client.getBeastRank(GLOOMFANG)).toBe(1); + // Genesis Beasts sit outside the ranked list. + expect(await client.getBeastRank(GENESIS_WARLOCK)).toBe(0); + }, 30_000); + + it('renders token_uri as base64 JSON with the expected identity', async () => { + const uri = await client.tokenUri(GLOOMFANG); + expect(uri.startsWith('data:application/json;base64,')).toBe(true); + + const json = JSON.parse( + Buffer.from(uri.slice('data:application/json;base64,'.length), 'base64').toString('utf8'), + ); + expect(json.name).toBe('"Agony Bane" Gloomfang'); + + const attribute = (t: string) => + json.attributes.find((a: { trait_type: string }) => a.trait_type === t)?.value; + expect(attribute('Beast')).toBe('Gloomfang'); + expect(attribute('Type')).toBe('Hunter'); + expect(attribute('Tier')).toBe('3'); + expect(attribute('Power')).toBe('30'); + expect(attribute('Genesis')).toBe('0'); + }, 60_000); + + it('fetches art from the species art provider', async () => { + const art = await client.getArt(decodeTokenId(GLOOMFANG)); + // animated = 1, shiny = 0, so the provider must return the regular GIF. + expect(art.startsWith('data:image/gif;base64,R0lGOD')).toBe(true); + }, 30_000); + + it('agrees with the chain on who owns the genesis token', async () => { + const owner = await client.ownerOf(GENESIS_WARLOCK); + expect(BigInt(owner)).not.toBe(0n); + }, 30_000); + + // ---------------------------------------------------- artist lookup + + const DEPLOYER = '0x736faa0dca6a4569bf22471b574ddf42107f5af81d67e2cb9e1aa9bba7de76b'; + + it('finds the species an artist controls', async () => { + // Walks the wallet's tokens through enumeration and keeps the Genesis + // Beasts. Non-empty is the assertion, not a detail: an enumeration + // entrypoint the contract lacks would look identical to "owns nothing". + const owned = await client.getSpeciesByArtist(DEPLOYER); + expect(owned.length).toBeGreaterThan(0); + expect(owned).toContain(76n); + }, 60_000); + + it('agrees with the registry on who the artist is', async () => { + const definition = await client.getDefinition(76n); + const genesis = await client.getGenesisTokenId(76n); + // The role is ownership of this token — the two must be the same address. + expect(BigInt(await client.ownerOf(genesis))).toBe(BigInt(definition.artist)); + }, 30_000); + + it('returns nothing for an address that never registered', async () => { + expect(await client.getSpeciesByArtist('0xdead')).toEqual([]); + }, 60_000); + + it('derives a real Genesis Beast token for each owned species', async () => { + const owned = await client.getOwnedSpecies(DEPLOYER); + expect(owned.length).toBeGreaterThan(0); + + for (const species of owned) { + // The derived ID must decode back to the same species with the traits + // the registry reports — that is what makes deriving it safe instead of + // reading it from the chain. + const beast = decodeTokenId(species.genesisTokenId); + expect(beast.id).toBe(species.beastId); + expect(beast.tier).toBe(species.definition.tier); + expect(beast.beastType).toBe(species.definition.beastType); + expect(beast.prefix).toBe(0); + expect(beast.suffix).toBe(0); + + // And it must actually exist: ownerOf reverts for an unminted token. + expect(BigInt(await client.ownerOf(species.genesisTokenId))).not.toBe(0n); + } + }, 60_000); +}); diff --git a/sdk/test/species.test.ts b/sdk/test/species.test.ts new file mode 100644 index 0000000..5115a25 --- /dev/null +++ b/sdk/test/species.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, it } from 'vitest'; +import { + BeastType, + GENESIS_SPECIES_NAMES, + PREFIX_NAMES, + SUFFIX_NAMES, + decodeTokenId, + fullBeastName, + genesisSpeciesName, + genesisTier, + genesisType, + prefixName, + suffixName, +} from '../src/index.js'; + +describe('generated tables', () => { + it('has the exact table sizes the contract does', () => { + expect(GENESIS_SPECIES_NAMES).toHaveLength(75); + expect(PREFIX_NAMES).toHaveLength(69); + expect(SUFFIX_NAMES).toHaveLength(18); + }); + + it('contains no empty entries', () => { + for (const table of [GENESIS_SPECIES_NAMES, PREFIX_NAMES, SUFFIX_NAMES]) { + expect(table.every((n) => n.length > 0)).toBe(true); + } + }); + + it('keeps every name inside a felt252', () => { + const encoder = new TextEncoder(); + for (const table of [GENESIS_SPECIES_NAMES, PREFIX_NAMES, SUFFIX_NAMES]) { + for (const name of table) expect(encoder.encode(name).length).toBeLessThanOrEqual(31); + } + }); +}); + +describe('genesis species traits', () => { + // Anchors taken from the contract's own unit tests in beast_manager.cairo. + it.each([ + [1n, 'Warlock', 1, BeastType.Magic], + [3n, 'Jiangshi', 1, BeastType.Magic], + [25n, 'Gnome', 5, BeastType.Magic], + [42n, 'Fenrir', 4, BeastType.Hunter], + [75n, undefined, 5, BeastType.Brute], + ])('resolves species %s', (id, name, tier, type) => { + if (name) expect(genesisSpeciesName(id as bigint)).toBe(name); + expect(genesisTier(id as bigint)).toBe(tier); + expect(genesisType(id as bigint)).toBe(type); + }); + + it('lays tiers out in five-wide groups within each type block', () => { + for (let id = 1; id <= 75; id++) { + const expected = Math.floor(((id - 1) % 25) / 5) + 1; + expect(genesisTier(BigInt(id))).toBe(expected); + } + }); + + it('splits types into three blocks of 25', () => { + expect(genesisType(25n)).toBe(BeastType.Magic); + expect(genesisType(26n)).toBe(BeastType.Hunter); + expect(genesisType(50n)).toBe(BeastType.Hunter); + expect(genesisType(51n)).toBe(BeastType.Brute); + }); + + it('refuses community species, which resolve through the registry', () => { + expect(() => genesisSpeciesName(76n)).toThrow(/not a genesis species/); + expect(() => genesisTier(76n)).toThrow(/not a genesis species/); + }); +}); + +describe('affixes', () => { + it('matches the contract anchors', () => { + expect(prefixName(1)).toBe('Agony'); + expect(suffixName(1)).toBe('Bane'); + expect(suffixName(2)).toBe('Root'); + }); + + it('returns null for the Genesis Beast’s absent affixes', () => { + expect(prefixName(0)).toBeNull(); + expect(suffixName(0)).toBeNull(); + }); + + it('rejects out-of-range affixes', () => { + expect(() => prefixName(70)).toThrow(RangeError); + expect(() => suffixName(19)).toThrow(RangeError); + }); +}); + +describe('full names', () => { + it('renders a genesis beast as the bare species name', () => { + expect(fullBeastName(decodeTokenId(0x7006400010000000000000000001n))).toBe('Warlock'); + }); + + it('reproduces the name the Sepolia contract rendered', () => { + // The deployed contract returned: "Agony Bane" Gloomfang + const beast = decodeTokenId(0x2e0064000a081000000000000004cn); + expect(fullBeastName(beast, 'Gloomfang')).toBe('"Agony Bane" Gloomfang'); + }); + + it('requires a species name for community species', () => { + const beast = decodeTokenId(0x2e0064000a081000000000000004cn); + expect(() => fullBeastName(beast)).toThrow(/not a genesis species/); + }); +}); diff --git a/sdk/test/tokenId.test.ts b/sdk/test/tokenId.test.ts new file mode 100644 index 0000000..804b482 --- /dev/null +++ b/sdk/test/tokenId.test.ts @@ -0,0 +1,172 @@ +import { describe, expect, it } from 'vitest'; +import { + BeastType, + TokenIdError, + beastPower, + decodeTokenId, + encodeTokenId, + genesisBeast, + isGenesis, + isGenesisSpecies, + type Beast, +} from '../src/index.js'; + +/** + * These two IDs were produced by the deployed contract on Sepolia, not by + * this SDK — see `docs/sepolia-v3-deployment.md`. They are the anchor that + * proves the TS layout matches `pack_to_u256`; everything else here is + * self-consistency. + */ +const ONCHAIN_GENESIS_WARLOCK = 0x7006400010000000000000000001n; +const ONCHAIN_GLOOMFANG = 0x2e0064000a081000000000000004cn; + +describe('on-chain parity', () => { + it('decodes the Sepolia genesis Warlock token', () => { + const beast = decodeTokenId(ONCHAIN_GENESIS_WARLOCK); + expect(beast).toEqual({ + id: 1n, + prefix: 0, + suffix: 0, + level: 1, + health: 100, + shiny: 1, + animated: 1, + tier: 1, + beastType: BeastType.Magic, + }); + expect(isGenesis(beast)).toBe(true); + }); + + it('decodes the Sepolia "Agony Bane" Gloomfang token', () => { + const beast = decodeTokenId(ONCHAIN_GLOOMFANG); + expect(beast).toEqual({ + id: 76n, + prefix: 1, + suffix: 1, + level: 10, + health: 100, + shiny: 0, + animated: 1, + tier: 3, + beastType: BeastType.Hunter, + }); + expect(isGenesis(beast)).toBe(false); + // The contract reported Power 30 for this token. + expect(beastPower(beast)).toBe(30); + }); + + it('re-encodes both on-chain tokens byte for byte', () => { + for (const tokenId of [ONCHAIN_GENESIS_WARLOCK, ONCHAIN_GLOOMFANG]) { + expect(encodeTokenId(decodeTokenId(tokenId))).toBe(tokenId); + } + }); +}); + +describe('round trips', () => { + it('survives the maximum value in every field', () => { + const beast: Beast = { + id: (1n << 64n) - 1n, + prefix: 69, + suffix: 18, + level: 65535, + health: 65535, + shiny: 1, + animated: 1, + tier: 5, + beastType: BeastType.Brute, + }; + expect(decodeTokenId(encodeTokenId(beast))).toEqual(beast); + }); + + it('stays inside 116 bits at maximum', () => { + const maxId = encodeTokenId({ + id: (1n << 64n) - 1n, + prefix: 69, + suffix: 18, + level: 65535, + health: 65535, + shiny: 1, + animated: 1, + tier: 5, + beastType: BeastType.Brute, + }); + expect(maxId >> 116n).toBe(0n); + // Fits a u128, which is what makes these IDs cheap for clients. + expect(maxId < 1n << 128n).toBe(true); + }); + + it('accepts hex strings and numbers', () => { + expect(decodeTokenId('0x7006400010000000000000000001').id).toBe(1n); + expect(decodeTokenId(ONCHAIN_GENESIS_WARLOCK.toString()).id).toBe(1n); + }); +}); + +describe('validation', () => { + const valid: Beast = { + id: 3n, + prefix: 1, + suffix: 2, + level: 10, + health: 100, + shiny: 0, + animated: 0, + tier: 1, + beastType: BeastType.Magic, + }; + + it('rejects a zero species ID', () => { + expect(() => encodeTokenId({ ...valid, id: 0n })).toThrow(TokenIdError); + expect(() => decodeTokenId(0n)).toThrow(/species ID must not be zero/); + }); + + it('rejects residual high bits', () => { + expect(() => decodeTokenId(1n << 116n)).toThrow(/residual high bits/); + }); + + it('rejects half-set affixes in both directions', () => { + // The (id, 0, 0) slot is reserved for the Genesis Beast, so a Beast may + // have both affixes or neither — never one. + expect(() => encodeTokenId({ ...valid, prefix: 0 })).toThrow(/invalid affix combo/); + expect(() => encodeTokenId({ ...valid, suffix: 0 })).toThrow(/invalid affix combo/); + }); + + it('accepts a genesis beast with neither affix', () => { + expect(() => encodeTokenId({ ...valid, prefix: 0, suffix: 0 })).not.toThrow(); + }); + + it('rejects out-of-range traits', () => { + expect(() => encodeTokenId({ ...valid, tier: 0 })).toThrow(/tier out of range/); + expect(() => encodeTokenId({ ...valid, tier: 6 })).toThrow(/tier out of range/); + expect(() => encodeTokenId({ ...valid, prefix: 70 })).toThrow(/prefix out of range/); + expect(() => encodeTokenId({ ...valid, suffix: 19 })).toThrow(/suffix out of range/); + expect(() => encodeTokenId({ ...valid, beastType: 3 as BeastType })).toThrow( + /beast type out of range/, + ); + }); + + it('rejects a decoded tier of zero', () => { + // Tier 0 is unreachable through encode, so build the ID by hand. + const raw = 1n | (1n << 64n) | (1n << 71n); + expect(() => decodeTokenId(raw)).toThrow(/tier out of range/); + }); +}); + +describe('derived helpers', () => { + it('caps power at u16', () => { + expect(beastPower({ ...genesisBeast(1n, 1, BeastType.Magic), level: 65535 })).toBe(65535); + }); + + it('computes power as level * (6 - tier)', () => { + for (let tier = 1; tier <= 5; tier++) { + const beast = { ...genesisBeast(1n, tier, BeastType.Magic), level: 10 }; + expect(beastPower(beast)).toBe(10 * (6 - tier)); + } + }); + + it('classifies genesis vs community species', () => { + expect(isGenesisSpecies(1n)).toBe(true); + expect(isGenesisSpecies(75n)).toBe(true); + expect(isGenesisSpecies(76n)).toBe(false); + expect(isGenesisSpecies(0n)).toBe(false); + }); +}); diff --git a/sdk/test/validation.test.ts b/sdk/test/validation.test.ts new file mode 100644 index 0000000..9d30959 --- /dev/null +++ b/sdk/test/validation.test.ts @@ -0,0 +1,122 @@ +import { describe, expect, it } from 'vitest'; +import { + validateArtSet, + validateFactoryArt, + validateRenderableArt, + validateSpeciesName, + validateTier, +} from '../src/index.js'; + +describe('species names', () => { + it('accepts the charset the contract allows', () => { + for (const name of ["Warlock", "Fire Drake", "K9", "Ol' One-Eye", 'x', 'A name that is 31 bytes long ok']) { + expect(validateSpeciesName(name).valid).toBe(true); + } + }); + + it('rejects the injection characters the contract guards against', () => { + // components_to_json and the SVG builder embed names unescaped, so these + // would break every token's metadata for that species. + for (const name of ['bad"name', 'bad\\name', '', 'a,b', 'ab']) { + expect(validateSpeciesName(name).valid).toBe(false); + } + }); + + it('rejects empty and edge-space names', () => { + expect(validateSpeciesName('').valid).toBe(false); + expect(validateSpeciesName(' Warlock').valid).toBe(false); + expect(validateSpeciesName('Warlock ').valid).toBe(false); + }); + + it('rejects names past the felt252 limit', () => { + expect(validateSpeciesName('a'.repeat(31)).valid).toBe(true); + expect(validateSpeciesName('a'.repeat(32)).valid).toBe(false); + }); + + it('counts bytes, not code points', () => { + // Multi-byte characters are outside the charset anyway, but the length + // check must not be fooled into thinking they fit. + expect(validateSpeciesName('é'.repeat(20)).valid).toBe(false); + }); +}); + +describe('renderable art', () => { + it('accepts every allowed media type', () => { + for (const uri of [ + 'data:image/png;base64,iVBORw0KGgo=', + 'data:image/gif;base64,R0lGODlhAQAB', + 'data:image/webp;base64,UklGRhIAAABX', + 'data:image/svg+xml;base64,PHN2Zy8+', + ]) { + expect(validateRenderableArt(uri).valid).toBe(true); + } + }); + + it('rejects non-image media types', () => { + expect(validateRenderableArt('data:text/html;base64,PHNjcmlwdD4=').valid).toBe(false); + }); + + it('rejects URL-encoded SVG, which carries raw markup', () => { + expect(validateRenderableArt('data:image/svg+xml,').valid).toBe(false); + }); + + it('rejects an attribute escape', () => { + // The art is embedded inside a single-quoted src='...' attribute. + expect(validateRenderableArt("data:image/png;base64,AAAA'AAA").valid).toBe(false); + }); + + it('rejects payloads that are not 4-aligned', () => { + expect(validateRenderableArt('data:image/png;base64,AAAAA').valid).toBe(false); + }); + + it('rejects an empty payload', () => { + expect(validateRenderableArt('data:image/png;base64,').valid).toBe(false); + }); + + it('rejects interior padding', () => { + expect(validateRenderableArt('data:image/png;base64,AA=ABBBB').valid).toBe(false); + }); +}); + +describe('factory art', () => { + it('accepts real PNG and both GIF versions', () => { + expect(validateFactoryArt('data:image/png;base64,iVBORw0KGgoAAAA1', 'png').valid).toBe(true); + expect(validateFactoryArt('data:image/gif;base64,R0lGODdhAAA1', 'gif').valid).toBe(true); + expect(validateFactoryArt('data:image/gif;base64,R0lGODlhAAA1', 'gif').valid).toBe(true); + }); + + it('rejects a payload without PNG magic bytes', () => { + expect(validateFactoryArt('data:image/png;base64,AAAAAAAAAAAA', 'png').valid).toBe(false); + }); + + it('rejects a truncated GIF signature', () => { + // "R0lGODAAAAAA" decodes to the invalid header GIF80; the version + // characters have to be checked, not just the "R0lGOD" stem. + expect(validateFactoryArt('data:image/gif;base64,R0lGODAAAAAA', 'gif').valid).toBe(false); + }); + + it('rejects a GIF submitted as a PNG', () => { + expect(validateFactoryArt('data:image/gif;base64,R0lGODdhAAA1', 'png').valid).toBe(false); + }); + + it('validates a whole set and names the offending variant', () => { + const good = { + pngRegular: 'data:image/png;base64,iVBORw0KGgoAAAA1', + pngShiny: 'data:image/png;base64,iVBORw0KGgoAAAA2', + gifRegular: 'data:image/gif;base64,R0lGODdhAAA1', + gifShiny: 'data:image/gif;base64,R0lGODdhAAA2', + }; + expect(validateArtSet(good).valid).toBe(true); + + const bad = validateArtSet({ ...good, gifShiny: 'data:image/gif;base64,AAAAAAAA' }); + expect(bad.valid).toBe(false); + expect(bad.error).toMatch(/^Shiny GIF:/); + }); +}); + +describe('tier', () => { + it('accepts 1 through 5 only', () => { + for (const tier of [1, 2, 3, 4, 5]) expect(validateTier(tier).valid).toBe(true); + for (const tier of [0, 6, -1, 1.5]) expect(validateTier(tier).valid).toBe(false); + }); +}); diff --git a/sdk/tsconfig.build.json b/sdk/tsconfig.build.json new file mode 100644 index 0000000..1c207e7 --- /dev/null +++ b/sdk/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src/**/*"] +} diff --git a/sdk/tsconfig.json b/sdk/tsconfig.json new file mode 100644 index 0000000..25b8ca1 --- /dev/null +++ b/sdk/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noUncheckedIndexedAccess": false, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "outDir": "dist" + }, + "include": ["src/**/*", "test/**/*"] +} diff --git a/sdk/vitest.config.ts b/sdk/vitest.config.ts new file mode 100644 index 0000000..43e56f4 --- /dev/null +++ b/sdk/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['test/**/*.test.ts'], + }, +});