From 23dec5b12c95defc733fa5c2bd23d83f19a370fb Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:13:29 +0200 Subject: [PATCH 01/15] feat(webapp): Safe-aware WithdrawPanel + extract SafeProposeFeedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WithdrawPanel now mirrors the SetFeesPanel pattern: when the proxy's fee-admin set contains a Safe, a "Propose via Safe" button appears next to the direct "Withdraw" button. Click → useSafePropose builds the SafeTx, signs via wagmi, posts to Den STS. Also extracts the proposed/error feedback block into a reusable SafeProposeFeedback component (renders nothing in idle, success card with safeTxHash + Den link, or one-line error). SetFeesPanel and WithdrawPanel both consume it — keeps copy + styling in sync as we diffuse the pattern to the other admin panels. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/SafeProposeFeedback.tsx | 41 ++++++++++++++ .../webapp/src/components/SetFeesPanel.tsx | 28 +--------- .../webapp/src/components/WithdrawPanel.tsx | 54 +++++++++++++++---- 3 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 packages/webapp/src/components/SafeProposeFeedback.tsx diff --git a/packages/webapp/src/components/SafeProposeFeedback.tsx b/packages/webapp/src/components/SafeProposeFeedback.tsx new file mode 100644 index 0000000..3d95c16 --- /dev/null +++ b/packages/webapp/src/components/SafeProposeFeedback.tsx @@ -0,0 +1,41 @@ +import type { SafeProposeResult } from '../hooks/useSafePropose' + +interface Props { + proposed: SafeProposeResult | null + error: string | null +} + +/** + * Shared success / error feedback block for any panel that uses + * useSafePropose. Renders the safeTxHash + Den link on success, or + * a one-line error if propose failed. Renders nothing in idle state. + */ +export function SafeProposeFeedback({ proposed, error }: Props) { + return ( + <> + {proposed && ( +
+
+ Proposed. safeTxHash:{' '} + {proposed.safeTxHash} +
+
+ Owners can co-sign and execute in{' '} + + Den + + . +
+
+ )} + {error && ( +

Safe propose: {error}

+ )} + + ) +} diff --git a/packages/webapp/src/components/SetFeesPanel.tsx b/packages/webapp/src/components/SetFeesPanel.tsx index b853bb4..481467f 100644 --- a/packages/webapp/src/components/SetFeesPanel.tsx +++ b/packages/webapp/src/components/SetFeesPanel.tsx @@ -6,6 +6,7 @@ import { ops } from '@intuition-fee-proxy/safe-tx' import { useSetFees } from '../hooks/useProxy' import { useSafeAdmin } from '../hooks/useSafeAdmin' import { useSafePropose } from '../hooks/useSafePropose' +import { SafeProposeFeedback } from './SafeProposeFeedback' interface Props { proxy: Address @@ -146,32 +147,7 @@ export function SetFeesPanel({ proxy, currentFixed, currentPct, onDone }: Props) - {safePropose.proposed && ( -
-
- Proposed. safeTxHash:{' '} - {safePropose.proposed.safeTxHash} -
-
- Owners can co-sign and execute in{' '} - - Den - - . -
-
- )} - - {safePropose.error && ( -

- Safe propose: {safePropose.error} -

- )} + {error && (

diff --git a/packages/webapp/src/components/WithdrawPanel.tsx b/packages/webapp/src/components/WithdrawPanel.tsx index b85ef94..82b7fd0 100644 --- a/packages/webapp/src/components/WithdrawPanel.tsx +++ b/packages/webapp/src/components/WithdrawPanel.tsx @@ -2,7 +2,11 @@ import { useEffect, useState } from 'react' import { formatEther, isAddress, parseEther, type Address } from 'viem' import { useAccount, useWaitForTransactionReceipt } from 'wagmi' +import { ops } from '@intuition-fee-proxy/safe-tx' import { useWithdraw } from '../hooks/useProxy' +import { useSafeAdmin } from '../hooks/useSafeAdmin' +import { useSafePropose } from '../hooks/useSafePropose' +import { SafeProposeFeedback } from './SafeProposeFeedback' interface Props { proxy: Address @@ -18,6 +22,9 @@ export function WithdrawPanel({ proxy, accumulated, onDone }: Props) { const { withdraw, hash, isPending, error } = useWithdraw(proxy) const receipt = useWaitForTransactionReceipt({ hash }) + const { safe } = useSafeAdmin(proxy) + const safePropose = useSafePropose({ safeAddress: safe }) + useEffect(() => { if (receipt.isSuccess) onDone() }, [hash, receipt.isSuccess]) @@ -34,12 +41,26 @@ export function WithdrawPanel({ proxy, accumulated, onDone }: Props) { } } + async function onProposeWithdraw() { + if (!toValid || !amountValid || !safe) return + safePropose.reset() + try { + await safePropose.propose( + ops.v2Admin.withdraw(proxy, to as Address, parseEther(amount)), + ) + } catch (e) { + console.error(e) + } + } + return (

Withdraw fees

- Pull accumulated fees to any address. Admin-only. + Pull accumulated fees to any address. Admin-only. Direct write + takes effect immediately. Safe propose opens a multisig + transaction for owners to co-sign in Den.

@@ -88,17 +109,32 @@ export function WithdrawPanel({ proxy, accumulated, onDone }: Props) { - +
+ + {safe && ( + + )} +
{isPending &&

Confirm in wallet…

} {receipt.isLoading &&

Mining…

} + + + {error && (

{error.message.split('\n')[0]} From 758ad3c6a1c03997269bd919f776300f2bae2738 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:15:26 +0200 Subject: [PATCH 02/15] feat(webapp): Safe-aware AdminsPanel (grant + per-row revoke) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the SetFees / Withdraw pattern but extended to handle the panel's two flows (grant new admin, revoke existing) and the new "Safe owner can propose without being a direct admin" case: - Grant form is now visible to (a) direct admins or (b) anyone when a Safe sits in the admins list — Safe owners who aren't direct admins still need to be able to propose - "Grant" direct button stays gated by connectedIsAdmin - "Propose via Safe" button shown when a Safe is in the admin list - Per-row: Direct "Revoke" gated by direct admin + last-admin guard; "Propose revoke via Safe" shown when Safe present + not last admin (the contract enforces last-admin guard regardless of caller) Single useSafePropose instance reused across grant + every revoke — the feedback block at the bottom shows the latest proposal regardless of which action triggered it. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../webapp/src/components/AdminsPanel.tsx | 146 ++++++++++++------ 1 file changed, 100 insertions(+), 46 deletions(-) diff --git a/packages/webapp/src/components/AdminsPanel.tsx b/packages/webapp/src/components/AdminsPanel.tsx index ad6ad2e..2cae0bb 100644 --- a/packages/webapp/src/components/AdminsPanel.tsx +++ b/packages/webapp/src/components/AdminsPanel.tsx @@ -2,13 +2,17 @@ import { useEffect, useState } from 'react' import { isAddress, type Address } from 'viem' import { useWaitForTransactionReceipt } from 'wagmi' +import { ops } from '@intuition-fee-proxy/safe-tx' import { useAdmins, useIsAdmin, useSetWhitelistedAdmin, } from '../hooks/useProxy' +import { useSafeAdmin } from '../hooks/useSafeAdmin' +import { useSafePropose } from '../hooks/useSafePropose' import AddressDisplay from './Address' import { SafeBadge } from './SafeBadge' +import { SafeProposeFeedback } from './SafeProposeFeedback' import { Spinner } from './Spinner' interface Props { @@ -28,18 +32,15 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { } = useSetWhitelistedAdmin(proxy) const receipt = useWaitForTransactionReceipt({ hash }) const [addDraft, setAddDraft] = useState('') - // Tracks which row (or 'ADD') owns the in-flight tx, so only that button - // shows "Signing…" / "Mining…" state. Other rows stay idle (but disabled). const [pendingTarget, setPendingTarget] = useState

( null, ) - // True between "receipt mined" and "admins list refetched" — bridges the - // gap where the tx is done but the event-log-derived list hasn't caught - // up yet. Drives the discrete title spinner + keeps the row button - // spinner alive so the UI never looks idle during that window. const [postTxRefreshing, setPostTxRefreshing] = useState(false) - const busy = isPending || receipt.isLoading || postTxRefreshing + const { safe } = useSafeAdmin(proxy) + const safePropose = useSafePropose({ safeAddress: safe }) + + const busy = isPending || receipt.isLoading || postTxRefreshing || safePropose.isProposing useEffect(() => { if (receipt.isSuccess) { @@ -50,7 +51,6 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { } }, [hash, receipt.isSuccess]) - // Release the spinner once the admins refetch settles. useEffect(() => { if (postTxRefreshing && !isLoading) { setPostTxRefreshing(false) @@ -58,8 +58,6 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { } }, [postTxRefreshing, isLoading]) - // If the write errored or the user rejected the signature, release the - // per-row pending marker so the buttons become clickable again. useEffect(() => { if ( !isPending && @@ -87,6 +85,18 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { } } + async function onProposeAdd() { + if (!addValid || !safe) return + safePropose.reset() + try { + await safePropose.propose( + ops.v2Admin.setWhitelistedAdmin(proxy, addDraft as Address, true), + ) + } catch (e) { + console.error(e) + } + } + async function onRevoke(addr: Address) { setPendingTarget(addr) try { @@ -97,6 +107,23 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { } } + async function onProposeRevoke(addr: Address) { + if (!safe) return + safePropose.reset() + try { + await safePropose.propose( + ops.v2Admin.setWhitelistedAdmin(proxy, addr, false), + ) + } catch (e) { + console.error(e) + } + } + + // The form is visible if the user can either direct-write (already an + // admin) or propose via Safe (a Safe is in the admin list — Safe owners + // who aren't direct admins can still propose). + const canInteract = connectedIsAdmin || Boolean(safe) + return (
@@ -142,7 +169,8 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { connectedAccount && addr.toLowerCase() === connectedAccount.toLowerCase() const isLastAdmin = admins.length === 1 - const canRevoke = connectedIsAdmin && !(isSelf && isLastAdmin) + const canDirectRevoke = connectedIsAdmin && !(isSelf && isLastAdmin) + const canProposeRevoke = Boolean(safe) && !isLastAdmin return (
  • )}
  • - {canRevoke && ( - - )} - {isSelf && isLastAdmin && ( - - Last admin — cannot self-revoke - - )} +
    + {canDirectRevoke && ( + + )} + {canProposeRevoke && ( + + )} + {isSelf && isLastAdmin && ( + + Last admin — cannot self-revoke + + )} +
    ) })} )} - {connectedIsAdmin ? ( + {canInteract ? (
    @@ -204,19 +244,31 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { placeholder="0x…" className="input font-mono text-xs flex-1 min-w-[260px]" /> - + {connectedIsAdmin && ( + + )} + {safe && ( + + )}
    {addDraft && !isAddress(addDraft) && (

    Invalid address.

    @@ -232,9 +284,11 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) {
    ) : (

    - Connect as a whitelisted admin to add or revoke admins. + Connect as a whitelisted admin (or as a Safe owner of a Safe in the admin list) to add or revoke admins.

    )} + +
    ) } From 3afaa6d394b82de5509b408a782de5a42cc4b105 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:18:55 +0200 Subject: [PATCH 03/15] feat(webapp): Safe-aware UpgradeAuthorityPanel + versionedProxy ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new ops namespace `versionedProxy` covering the Role 1 (proxyAdmin) operations on IntuitionVersionedFeeProxy: - transferProxyAdmin(proxy, newAdmin) — initiates 2-step rotation - acceptProxyAdmin(proxy) — completes from the new owner side - registerVersion(proxy, version, impl) — used by VersionsPanel next - setDefaultVersion(proxy, version) — same UpgradeAuthorityPanel now detects whether proxyAdmin (or the pending proxyAdmin) is itself a Safe via useSafeStatus, and exposes "Propose via Safe" buttons accordingly: - Grant form: visible to direct admin OR when proxyAdmin is a Safe (Safe owners need to propose); Direct "Grant" stays gated by isYou, "Propose via Safe" gated by proxyAdminSafe presence - Pending area: in addition to the direct "Accept proxyAdmin role" for an EOA pending owner, surfaces "Propose acceptProxyAdmin via Safe" when the pending owner is itself a Safe (the standard handoff path when rotating Role 1 to a Safe) Single useSafePropose instance + SafeProposeFeedback at the bottom. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/safe-tx/src/ops/index.ts | 1 + packages/safe-tx/src/ops/versioned-proxy.ts | 99 +++++++++++++++ .../src/components/UpgradeAuthorityPanel.tsx | 120 +++++++++++++----- 3 files changed, 185 insertions(+), 35 deletions(-) create mode 100644 packages/safe-tx/src/ops/versioned-proxy.ts diff --git a/packages/safe-tx/src/ops/index.ts b/packages/safe-tx/src/ops/index.ts index aaa888b..3d7ece7 100644 --- a/packages/safe-tx/src/ops/index.ts +++ b/packages/safe-tx/src/ops/index.ts @@ -1,3 +1,4 @@ export * as v2Admin from './v2-admin.js' export * as factory from './factory.js' export * as uups from './uups-upgrade.js' +export * as versionedProxy from './versioned-proxy.js' diff --git a/packages/safe-tx/src/ops/versioned-proxy.ts b/packages/safe-tx/src/ops/versioned-proxy.ts new file mode 100644 index 0000000..1c56548 --- /dev/null +++ b/packages/safe-tx/src/ops/versioned-proxy.ts @@ -0,0 +1,99 @@ +import { encodeFunctionData, type Address, type Hex } from 'viem' +import type { AdminOp } from '../types.js' + +/** + * Role 1 (proxyAdmin) operations on `IntuitionVersionedFeeProxy`. + * + * `transferProxyAdmin` is 2-step: it sets `pendingProxyAdmin`, the + * target then has to call `acceptProxyAdmin` from their own wallet + * (or via their Safe propose flow if they're a Safe) to finalize. + */ +const VERSIONED_PROXY_ABI = [ + { + type: 'function', + name: 'transferProxyAdmin', + inputs: [{ name: 'newAdmin', type: 'address' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'acceptProxyAdmin', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'registerVersion', + inputs: [ + { name: 'version', type: 'bytes32' }, + { name: 'implementation', type: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setDefaultVersion', + inputs: [{ name: 'version', type: 'bytes32' }], + outputs: [], + stateMutability: 'nonpayable', + }, +] as const + +export function transferProxyAdmin(proxy: Address, newAdmin: Address): AdminOp { + return { + to: proxy, + value: 0n, + data: encodeFunctionData({ + abi: VERSIONED_PROXY_ABI, + functionName: 'transferProxyAdmin', + args: [newAdmin], + }), + description: `transferProxyAdmin(-> ${newAdmin}) on proxy ${proxy}`, + } +} + +export function acceptProxyAdmin(proxy: Address): AdminOp { + return { + to: proxy, + value: 0n, + data: encodeFunctionData({ + abi: VERSIONED_PROXY_ABI, + functionName: 'acceptProxyAdmin', + args: [], + }), + description: `acceptProxyAdmin() on proxy ${proxy}`, + } +} + +export function registerVersion( + proxy: Address, + version: Hex, + implementation: Address, +): AdminOp { + return { + to: proxy, + value: 0n, + data: encodeFunctionData({ + abi: VERSIONED_PROXY_ABI, + functionName: 'registerVersion', + args: [version, implementation], + }), + description: `registerVersion(${version}, ${implementation}) on proxy ${proxy}`, + } +} + +export function setDefaultVersion(proxy: Address, version: Hex): AdminOp { + return { + to: proxy, + value: 0n, + data: encodeFunctionData({ + abi: VERSIONED_PROXY_ABI, + functionName: 'setDefaultVersion', + args: [version], + }), + description: `setDefaultVersion(${version}) on proxy ${proxy}`, + } +} diff --git a/packages/webapp/src/components/UpgradeAuthorityPanel.tsx b/packages/webapp/src/components/UpgradeAuthorityPanel.tsx index dbdfcdb..9b2f395 100644 --- a/packages/webapp/src/components/UpgradeAuthorityPanel.tsx +++ b/packages/webapp/src/components/UpgradeAuthorityPanel.tsx @@ -2,15 +2,19 @@ import { useEffect, useState } from 'react' import { isAddress, type Address } from 'viem' import { useWaitForTransactionReceipt } from 'wagmi' +import { ops } from '@intuition-fee-proxy/safe-tx' import { useAcceptProxyAdmin, useTransferProxyAdmin, } from '../hooks/useVersionedProxy' +import { useSafePropose } from '../hooks/useSafePropose' +import { useSafeStatus } from '../hooks/useSafeStatus' import AddressDisplay from './Address' import { useProxyAdminRotation } from '../hooks/useProxyAdminRotation' import { usePostTxRefreshing } from '../hooks/usePostTxRefreshing' import { ProxyAdminSafeBanner } from './ProxyAdminSafeBanner' import { SafeBadge } from './SafeBadge' +import { SafeProposeFeedback } from './SafeProposeFeedback' import { Spinner } from './Spinner' const ZERO = '0x0000000000000000000000000000000000000000' @@ -49,6 +53,18 @@ export function UpgradeAuthorityPanel({ ) const hasBothRoles = isYou && isConnectedFeeAdmin + // Detect if proxyAdmin is itself a Safe — then we can propose + // transferProxyAdmin / acceptProxyAdmin via that Safe. + const proxyAdminStatus = useSafeStatus(proxyAdmin) + const proxyAdminSafe = proxyAdminStatus.kind === 'safe' ? proxyAdmin : undefined + // Same for the pending admin — useful when the new owner is a Safe + // and needs to acceptProxyAdmin via that Safe's quorum. + const pendingStatus = useSafeStatus(pendingProxyAdmin) + const pendingIsSafe = pendingStatus.kind === 'safe' + const safePropose = useSafePropose({ + safeAddress: proxyAdminSafe ?? (pendingIsSafe ? pendingProxyAdmin : undefined), + }) + // Standalone "Grant Role 1" — separate write instance from the rotation. const [newAdminInput, setNewAdminInput] = useState('') const { @@ -139,26 +155,17 @@ export function UpgradeAuthorityPanel({

    - Only one address can hold this role at a time. - For production, put a Gnosis Safe multisig here — - the Safe itself handles N signers / threshold / signer rotation - internally, so you get “multi-human proxyAdmin” - without the contract knowing anything about it. + Single slot. Controls implementation registration only — fees and + withdrawals are Role 2. Use a Safe for production.

    {acceptedFlash && ( -
    - - ✓ - +
    - You are now proxyAdmin — the transfer has been - finalised on-chain. + You are now proxyAdmin — transfer finalised + on-chain.
    )} @@ -199,10 +206,9 @@ export function UpgradeAuthorityPanel({ )}

    - A transfer has been initiated. The pending address must sign{' '} - acceptProxyAdmin() from - their wallet to finalise. Until then the current admin keeps all - powers and can overwrite the pending candidate. + Pending must call{' '} + acceptProxyAdmin() to + finalise. Current admin can still overwrite.

    {isPendingYou && ( + )} {acceptError && (

    {acceptError.message.split('\n')[0]} @@ -226,7 +249,7 @@ export function UpgradeAuthorityPanel({ )} - {isYou && ( + {(isYou || proxyAdminSafe) && (

    {transferError && (

    @@ -273,6 +321,8 @@ export function UpgradeAuthorityPanel({ {hasBothRoles && } + +

    Role 1 controls which logic the proxy delegates to — register new implementations, change default version, rename. It{' '} From 531560d685f1200a7ad723fe0082bc02a55373e5 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:28:20 +0200 Subject: [PATCH 04/15] feat(webapp): Safe-aware VersionsPanel (registerVersion + setDefaultVersion) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reads proxyAdmin from chain via useReadContract + IntuitionVersionedFeeProxyABI (no prop plumbing through ProxyDetail/OverviewTab needed). When the proxyAdmin resolves to a known Safe singleton, surfaces "Propose via Safe" alternatives in each VersionRow: - "Propose register via Safe" on rows with status === 'available' (in parallel to the direct "Promote" button when the user is also the direct proxyAdmin) - "Propose via Safe" on rows with status === 'registered' (set as default), in parallel to the direct "Make default" button Both proposed ops use the new ops.versionedProxy.registerVersion / setDefaultVersion builders, post via api-kit to Den STS, and surface the safeTxHash + Den link via SafeProposeFeedback at the panel level. Bottom-of-panel hint updated: "Registering or promoting versions is proxy-admin only" now switches to "The proxyAdmin is a Safe — actions above open a multisig proposal" when applicable. AdvancedCustomPaste section stays gated by direct isProxyAdmin only — Safe propose for arbitrary paste is non-trivial (input would need to plumb through useSafePropose with a custom AdminOp builder); deferred. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../webapp/src/components/VersionsPanel.tsx | 98 +++++++++++++++++-- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/packages/webapp/src/components/VersionsPanel.tsx b/packages/webapp/src/components/VersionsPanel.tsx index ca65a5f..adb2387 100644 --- a/packages/webapp/src/components/VersionsPanel.tsx +++ b/packages/webapp/src/components/VersionsPanel.tsx @@ -1,18 +1,23 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { isAddress, stringToHex, type Address, type Hex } from 'viem' -import { useWaitForTransactionReceipt } from 'wagmi' +import { useReadContract, useWaitForTransactionReceipt } from 'wagmi' import { + IntuitionVersionedFeeProxyABI, listVersionsByFamily, type CanonicalVersion, type NetworkName, type ProxyFamily, } from '@intuition-fee-proxy/sdk' +import { ops } from '@intuition-fee-proxy/safe-tx' import { useRegisterVersion, useSetDefaultVersion, } from '../hooks/useVersionedProxy' +import { useSafePropose } from '../hooks/useSafePropose' +import { useSafeStatus } from '../hooks/useSafeStatus' import { decodeVersion } from '../lib/format' +import { SafeProposeFeedback } from './SafeProposeFeedback' interface Props { proxy: Address @@ -120,6 +125,43 @@ export function VersionsPanel({ const registerReceipt = useWaitForTransactionReceipt({ hash: registerHash }) const defaultReceipt = useWaitForTransactionReceipt({ hash: defaultHash }) + // Read the current proxyAdmin so we can detect whether it's a Safe + // (and surface "Propose via Safe" buttons when a Safe holds Role 1). + const proxyAdminRead = useReadContract({ + abi: IntuitionVersionedFeeProxyABI, + address: proxy, + functionName: 'proxyAdmin', + }) + const proxyAdmin = proxyAdminRead.data as Address | undefined + const proxyAdminStatus = useSafeStatus(proxyAdmin) + const proxyAdminSafe = + proxyAdminStatus.kind === 'safe' ? proxyAdmin : undefined + const safePropose = useSafePropose({ safeAddress: proxyAdminSafe }) + + async function onProposePromote(row: Row) { + if (!row.impl || !proxyAdminSafe) return + safePropose.reset() + try { + await safePropose.propose( + ops.versionedProxy.registerVersion(proxy, row.labelHex, row.impl), + ) + } catch (e) { + console.error(e) + } + } + + async function onProposeMakeDefault(row: Row) { + if (!proxyAdminSafe) return + safePropose.reset() + try { + await safePropose.propose( + ops.versionedProxy.setDefaultVersion(proxy, row.labelHex), + ) + } catch (e) { + console.error(e) + } + } + // Tracks the label being *promoted* (register → auto-setDefault chain). // Also used by "Make default" clicks (they skip the register step by // starting with status === 'registered'). @@ -266,11 +308,15 @@ export function VersionsPanel({ key={row.label} row={row} isProxyAdmin={isProxyAdmin} + proxyAdminIsSafe={Boolean(proxyAdminSafe)} busy={isBusyFor(row)} busyLabel={busyLabelFor(row)} anyBusy={pendingStage !== 'idle'} + safeProposing={safePropose.isProposing} onPromote={() => onPromote(row)} onMakeDefault={() => onMakeDefault(row)} + onProposePromote={() => onProposePromote(row)} + onProposeMakeDefault={() => onProposeMakeDefault(row)} /> ))} @@ -281,6 +327,8 @@ export function VersionsPanel({

    )} + + {isProxyAdmin && ( )} - {!isProxyAdmin && ( + {!isProxyAdmin && !proxyAdminSafe && (

    Registering or promoting versions is proxy-admin only.

    )} + {!isProxyAdmin && proxyAdminSafe && ( +

    + The proxyAdmin is a Safe — register / promote actions above + open a multisig proposal. Co-sign and execute in Den. +

    + )}
    ) } @@ -301,19 +355,27 @@ export function VersionsPanel({ function VersionRow({ row, isProxyAdmin, + proxyAdminIsSafe, busy, busyLabel, anyBusy, + safeProposing, onPromote, onMakeDefault, + onProposePromote, + onProposeMakeDefault, }: { row: Row isProxyAdmin: boolean + proxyAdminIsSafe: boolean busy: boolean busyLabel: string anyBusy: boolean + safeProposing: boolean onPromote: () => void onMakeDefault: () => void + onProposePromote: () => void + onProposeMakeDefault: () => void }) { const impl = row.impl ?? '—' @@ -353,8 +415,8 @@ function VersionRow({ )} - {isProxyAdmin && ( -
    + {(isProxyAdmin || proxyAdminIsSafe) && ( +
    {row.status.kind === 'default' && ( )} - {row.status.kind === 'registered' && ( + {row.status.kind === 'registered' && isProxyAdmin && ( )} - {row.status.kind === 'available' && ( + {row.status.kind === 'registered' && proxyAdminIsSafe && ( + + )} + {row.status.kind === 'available' && isProxyAdmin && ( )} + {row.status.kind === 'available' && proxyAdminIsSafe && ( + + )}
    )} From 534a0dd86d32b4a0d3b8043a041ed4f36864a039 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:32:01 +0200 Subject: [PATCH 05/15] feat(webapp): Safe-aware sponsored panels (Reclaim + ClaimLimits) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the missing sponsored AdminOp builders to safe-tx and surfaces "Propose via Safe" alternatives on the two admin-gated sponsored panels: src/ops/sponsored.ts (new namespace): - setClaimLimits(proxy, maxPerTx, maxPerWindow, maxVolumePerWindow, windowSec) - reclaimFromPool(proxy, amount, to) Both are gated by onlyWhitelistedAdmin on V2Sponsored. ReclaimFromPoolPanel: same pattern as Withdraw — direct + propose buttons, SafeProposeFeedback at the bottom. ClaimLimitsPanel: same pattern, four-arg builder. FundPoolPanel intentionally untouched — fundPool() is `payable` and NOT admin-gated (anyone can credit the pool). Safe-via-treasury funding would require allowing value > 0n in AdminOp (currently all builders set value: 0n), deferred as a separate refactor. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/safe-tx/src/ops/index.ts | 1 + packages/safe-tx/src/ops/sponsored.ts | 70 +++++++++++++++++++ .../src/components/ClaimLimitsPanel.tsx | 55 ++++++++++++--- .../src/components/ReclaimFromPoolPanel.tsx | 49 ++++++++++--- 4 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 packages/safe-tx/src/ops/sponsored.ts diff --git a/packages/safe-tx/src/ops/index.ts b/packages/safe-tx/src/ops/index.ts index 3d7ece7..d10d23a 100644 --- a/packages/safe-tx/src/ops/index.ts +++ b/packages/safe-tx/src/ops/index.ts @@ -2,3 +2,4 @@ export * as v2Admin from './v2-admin.js' export * as factory from './factory.js' export * as uups from './uups-upgrade.js' export * as versionedProxy from './versioned-proxy.js' +export * as sponsored from './sponsored.js' diff --git a/packages/safe-tx/src/ops/sponsored.ts b/packages/safe-tx/src/ops/sponsored.ts new file mode 100644 index 0000000..76399b5 --- /dev/null +++ b/packages/safe-tx/src/ops/sponsored.ts @@ -0,0 +1,70 @@ +import { encodeFunctionData, type Address } from 'viem' +import type { AdminOp } from '../types.js' + +/** + * Admin operations specific to `IntuitionFeeProxyV2Sponsored` (the + * sponsor-pool-funded variant). All gated by `onlyWhitelistedAdmin`. + * + * Note: `fundPool` is `external payable` and NOT admin-gated — anyone + * can credit the sponsor pool — so it's not modelled as an AdminOp. + */ +const SPONSORED_ABI = [ + { + type: 'function', + name: 'setClaimLimits', + inputs: [ + { name: 'maxPerTx', type: 'uint256' }, + { name: 'maxPerWindow', type: 'uint256' }, + { name: 'maxVolumePerWindow', type: 'uint256' }, + { name: 'windowSec', type: 'uint256' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'reclaimFromPool', + inputs: [ + { name: 'amount', type: 'uint256' }, + { name: 'to', type: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, +] as const + +export function setClaimLimits( + proxy: Address, + maxPerTx: bigint, + maxPerWindow: bigint, + maxVolumePerWindow: bigint, + windowSec: bigint, +): AdminOp { + return { + to: proxy, + value: 0n, + data: encodeFunctionData({ + abi: SPONSORED_ABI, + functionName: 'setClaimLimits', + args: [maxPerTx, maxPerWindow, maxVolumePerWindow, windowSec], + }), + description: `setClaimLimits(maxPerTx=${maxPerTx}, maxPerWindow=${maxPerWindow}, maxVolumePerWindow=${maxVolumePerWindow}, windowSec=${windowSec}) on ${proxy}`, + } +} + +export function reclaimFromPool( + proxy: Address, + amount: bigint, + to: Address, +): AdminOp { + return { + to: proxy, + value: 0n, + data: encodeFunctionData({ + abi: SPONSORED_ABI, + functionName: 'reclaimFromPool', + args: [amount, to], + }), + description: `reclaimFromPool(${amount} wei -> ${to}) on ${proxy}`, + } +} diff --git a/packages/webapp/src/components/ClaimLimitsPanel.tsx b/packages/webapp/src/components/ClaimLimitsPanel.tsx index 7218d02..086cd85 100644 --- a/packages/webapp/src/components/ClaimLimitsPanel.tsx +++ b/packages/webapp/src/components/ClaimLimitsPanel.tsx @@ -2,8 +2,12 @@ import { useEffect, useState } from 'react' import { formatEther, parseEther, type Address } from 'viem' import { useWaitForTransactionReceipt } from 'wagmi' +import { ops } from '@intuition-fee-proxy/safe-tx' import { useSetClaimLimits } from '../hooks/useSponsoredProxy' +import { useSafeAdmin } from '../hooks/useSafeAdmin' +import { useSafePropose } from '../hooks/useSafePropose' import { WINDOW_PRESETS, formatWindow } from '../lib/format' +import { SafeProposeFeedback } from './SafeProposeFeedback' interface Limits { maxClaimPerTx: bigint @@ -39,6 +43,9 @@ export function ClaimLimitsPanel({ proxy, current, onDone }: Props) { useSetClaimLimits(proxy) const receipt = useWaitForTransactionReceipt({ hash }) + const { safe } = useSafeAdmin(proxy) + const safePropose = useSafePropose({ safeAddress: safe }) + useEffect(() => { if (current) { setMaxPerTx(formatEther(current.maxClaimPerTx)) @@ -85,6 +92,24 @@ export function ClaimLimitsPanel({ proxy, current, onDone }: Props) { } } + async function onProposeSetLimits() { + if (!allValid || !safe) return + safePropose.reset() + try { + await safePropose.propose( + ops.sponsored.setClaimLimits( + proxy, + parseEther(maxPerTx), + BigInt(maxPerWindow), + parseEther(maxVolume), + BigInt(windowSec), + ), + ) + } catch (e) { + console.error(e) + } + } + // Live recap values const recapTx = toNumberOrUndefined(maxPerTx) const recapCount = toNumberOrUndefined(maxPerWindow) @@ -285,14 +310,28 @@ export function ClaimLimitsPanel({ proxy, current, onDone }: Props) {

    - +
    + + {safe && ( + + )} +
    + + {error && (

    diff --git a/packages/webapp/src/components/ReclaimFromPoolPanel.tsx b/packages/webapp/src/components/ReclaimFromPoolPanel.tsx index 3ef1c5a..ea294e4 100644 --- a/packages/webapp/src/components/ReclaimFromPoolPanel.tsx +++ b/packages/webapp/src/components/ReclaimFromPoolPanel.tsx @@ -2,7 +2,11 @@ import { useEffect, useState } from 'react' import { formatEther, isAddress, parseEther, type Address } from 'viem' import { useWaitForTransactionReceipt } from 'wagmi' +import { ops } from '@intuition-fee-proxy/safe-tx' import { useReclaimFromPool } from '../hooks/useSponsoredProxy' +import { useSafeAdmin } from '../hooks/useSafeAdmin' +import { useSafePropose } from '../hooks/useSafePropose' +import { SafeProposeFeedback } from './SafeProposeFeedback' interface Props { proxy: Address @@ -16,6 +20,9 @@ export function ReclaimFromPoolPanel({ proxy, poolBalance, onDone }: Props) { const { reclaim, hash, isPending, error, reset } = useReclaimFromPool(proxy) const receipt = useWaitForTransactionReceipt({ hash }) + const { safe } = useSafeAdmin(proxy) + const safePropose = useSafePropose({ safeAddress: safe }) + useEffect(() => { if (receipt.isSuccess) { onDone() @@ -36,6 +43,18 @@ export function ReclaimFromPoolPanel({ proxy, poolBalance, onDone }: Props) { } } + async function onProposeReclaim() { + if (!toValid || !amountValid || !safe) return + safePropose.reset() + try { + await safePropose.propose( + ops.sponsored.reclaimFromPool(proxy, parseEther(amount), to as Address), + ) + } catch (e) { + console.error(e) + } + } + return (

    @@ -77,14 +96,28 @@ export function ReclaimFromPoolPanel({ proxy, poolBalance, onDone }: Props) { )} - +
    + + {safe && ( + + )} +
    + + {error && (

    From 7d0896ac7bdc89a586f4bf4baad97af2c435980a Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:38:30 +0200 Subject: [PATCH 06/15] test(safe-tx): unit cover new ops + smoke test rotation script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the test gap for the two AdminOp namespaces added during the webapp Safe-aware diffusion: test/unit/ops/versioned-proxy.test.ts (5 assertions): - transferProxyAdmin / acceptProxyAdmin / registerVersion / setDefaultVersion: selector match, ABI round-trip via decodeFunctionData, value == 0n, target == proxy test/unit/ops/sponsored.test.ts (3 assertions): - setClaimLimits encoding (4 uint256 args in canonical order) - reclaimFromPool encoding (amount + recipient) - description includes recipient + amount for log traceability test/integration/rotation-script.test.ts (3 smoke tests via spawnSync): - `bun transferAdminToSafe.ts --help` exits 0 with usage - missing --proxy surfaces a clear "required option" error - --dry-run runs without PROPOSER_PK env (just network revert is OK) Full e2e rotation against a live mock proxy on Anvil fork is deliberately out of scope for this commit — the rotation mechanics (EOA setWhitelistedAdmin + Safe execTransaction) are already covered by direct-sign.test.ts and v2-admin.test.ts. Adding a deployable mock contract would need ~1-2h of solc/forge plumbing for marginal extra coverage; deferred until a real proxy lands on mainnet for true end-to-end validation. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../test/integration/rotation-script.test.ts | 90 +++++++++++++++++++ .../safe-tx/test/unit/ops/sponsored.test.ts | 65 ++++++++++++++ .../test/unit/ops/versioned-proxy.test.ts | 71 +++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 packages/safe-tx/test/integration/rotation-script.test.ts create mode 100644 packages/safe-tx/test/unit/ops/sponsored.test.ts create mode 100644 packages/safe-tx/test/unit/ops/versioned-proxy.test.ts diff --git a/packages/safe-tx/test/integration/rotation-script.test.ts b/packages/safe-tx/test/integration/rotation-script.test.ts new file mode 100644 index 0000000..c895338 --- /dev/null +++ b/packages/safe-tx/test/integration/rotation-script.test.ts @@ -0,0 +1,90 @@ +import { spawnSync } from 'node:child_process' +import { resolve } from 'node:path' +import { describe, expect, it } from 'vitest' + +/** + * Smoke tests for the transferAdminToSafe.ts CLI script. We don't run + * the full rotation against a real proxy here (would require deploying + * a mock V2 proxy on Anvil fork, ~1-2h of plumbing). Instead we verify + * the script's process-level behavior: + * + * - --help prints usage and exits 0 + * - missing required flags surface clear errors + * - --dry-run can run without PROPOSER_PK env var set + * + * The on-chain rotation mechanics are covered by: + * - test/integration/direct-sign.test.ts (Safe execTransaction flow) + * - test/integration/api-kit.test.ts (STS propose flow) + * - test/unit/ops/v2-admin.test.ts (setWhitelistedAdmin builder) + */ + +const SCRIPT = resolve( + __dirname, + '..', + '..', + 'scripts', + 'transferAdminToSafe.ts', +) + +function runScript(args: string[], env: Record = {}): { + stdout: string + stderr: string + status: number | null +} { + const result = spawnSync('bun', [SCRIPT, ...args], { + encoding: 'utf-8', + env: { ...process.env, ...env }, + timeout: 30_000, + }) + return { + stdout: result.stdout ?? '', + stderr: result.stderr ?? '', + status: result.status, + } +} + +describe('transferAdminToSafe script — smoke', () => { + it('--help exits 0 and prints usage', () => { + const { stdout, status } = runScript(['--help']) + expect(status).toBe(0) + expect(stdout).toContain('transferAdminToSafe') + expect(stdout).toContain('--proxy') + expect(stdout).toContain('--safe') + expect(stdout).toContain('--eoa') + expect(stdout).toContain('--dry-run') + }) + + it('missing --proxy fails with a clear required-option error', () => { + const { stderr, status } = runScript([ + '--safe', + '0xf10D442D0fB934D4037DC30769a6EfCf2f54F7B6', + '--eoa', + '0xc634457aD68b037E2D5aA1C10c3930d7e4E2d551', + ]) + expect(status).not.toBe(0) + expect(stderr).toMatch(/required option/i) + expect(stderr).toContain('--proxy') + }) + + it('--dry-run does not require PROPOSER_PK env var', () => { + // Use a non-existent proxy address — the dry-run still hits the + // network to read whitelistedAdmins (which will revert because no + // contract). We expect a non-zero exit but NOT the missing-key error. + const { stderr, status } = runScript( + [ + '--proxy', + '0x000000000000000000000000000000000000dEaD', + '--safe', + '0xf10D442D0fB934D4037DC30769a6EfCf2f54F7B6', + '--eoa', + '0xc634457aD68b037E2D5aA1C10c3930d7e4E2d551', + '--dry-run', + ], + { PROPOSER_PK: '' }, + ) + // We don't care about the network outcome — only that the script + // didn't bail on the env-var check. + expect(status).not.toBe(0) + expect(stderr).not.toMatch(/PROPOSER_PK env var required/) + }, 30_000) +}) diff --git a/packages/safe-tx/test/unit/ops/sponsored.test.ts b/packages/safe-tx/test/unit/ops/sponsored.test.ts new file mode 100644 index 0000000..e966c1f --- /dev/null +++ b/packages/safe-tx/test/unit/ops/sponsored.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from 'vitest' +import { decodeFunctionData, getAddress, toFunctionSelector } from 'viem' +import * as sponsored from '../../../src/ops/sponsored.js' + +const PROXY = getAddress('0xf10D442D0fB934D4037DC30769a6EfCf2f54F7B6') +const RECIPIENT = getAddress('0xc634457aD68b037E2D5aA1C10c3930d7e4E2d551') + +const SELECTOR_SET_LIMITS = toFunctionSelector( + 'setClaimLimits(uint256,uint256,uint256,uint256)', +) +const SELECTOR_RECLAIM = toFunctionSelector('reclaimFromPool(uint256,address)') + +const ABI = [ + { + type: 'function', + name: 'setClaimLimits', + inputs: [ + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint256' }, + { type: 'uint256' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'reclaimFromPool', + inputs: [{ type: 'uint256' }, { type: 'address' }], + outputs: [], + stateMutability: 'nonpayable', + }, +] as const + +describe('sponsored AdminOp builders', () => { + it('setClaimLimits encodes 4 uint256 args in order', () => { + const op = sponsored.setClaimLimits( + PROXY, + 10n ** 18n, // maxPerTx + 10n, // maxPerWindow + 10n ** 19n, // maxVolumePerWindow + 3600n, // windowSec + ) + expect(op.to).toBe(PROXY) + expect(op.value).toBe(0n) + expect(op.data.slice(0, 10)).toBe(SELECTOR_SET_LIMITS) + const decoded = decodeFunctionData({ abi: ABI, data: op.data }) + expect(decoded.functionName).toBe('setClaimLimits') + expect(decoded.args).toEqual([10n ** 18n, 10n, 10n ** 19n, 3600n]) + }) + + it('reclaimFromPool encodes (amount, to)', () => { + const op = sponsored.reclaimFromPool(PROXY, 5n * 10n ** 18n, RECIPIENT) + expect(op.data.slice(0, 10)).toBe(SELECTOR_RECLAIM) + const decoded = decodeFunctionData({ abi: ABI, data: op.data }) + expect(decoded.functionName).toBe('reclaimFromPool') + expect(decoded.args).toEqual([5n * 10n ** 18n, RECIPIENT]) + }) + + it('description includes recipient + amount for traceability', () => { + const op = sponsored.reclaimFromPool(PROXY, 1n, RECIPIENT) + expect(op.description).toContain(RECIPIENT) + expect(op.description).toContain('1 wei') + }) +}) diff --git a/packages/safe-tx/test/unit/ops/versioned-proxy.test.ts b/packages/safe-tx/test/unit/ops/versioned-proxy.test.ts new file mode 100644 index 0000000..fa236ad --- /dev/null +++ b/packages/safe-tx/test/unit/ops/versioned-proxy.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, it } from 'vitest' +import { decodeFunctionData, getAddress, stringToHex, toFunctionSelector } from 'viem' +import * as vp from '../../../src/ops/versioned-proxy.js' + +const PROXY = getAddress('0xf10D442D0fB934D4037DC30769a6EfCf2f54F7B6') +const NEW_ADMIN = getAddress('0xc634457aD68b037E2D5aA1C10c3930d7e4E2d551') +const NEW_IMPL = getAddress('0x29fcB43b46531BcA003ddC8FCB67FFE91900C762') +const VERSION_BYTES32 = stringToHex('v3.0.0', { size: 32 }) + +const SELECTOR_TRANSFER = toFunctionSelector('transferProxyAdmin(address)') +const SELECTOR_ACCEPT = toFunctionSelector('acceptProxyAdmin()') +const SELECTOR_REGISTER = toFunctionSelector('registerVersion(bytes32,address)') +const SELECTOR_SET_DEFAULT = toFunctionSelector('setDefaultVersion(bytes32)') + +const ABI = [ + { type: 'function', name: 'transferProxyAdmin', inputs: [{ type: 'address' }], outputs: [], stateMutability: 'nonpayable' }, + { type: 'function', name: 'acceptProxyAdmin', inputs: [], outputs: [], stateMutability: 'nonpayable' }, + { type: 'function', name: 'registerVersion', inputs: [{ type: 'bytes32' }, { type: 'address' }], outputs: [], stateMutability: 'nonpayable' }, + { type: 'function', name: 'setDefaultVersion', inputs: [{ type: 'bytes32' }], outputs: [], stateMutability: 'nonpayable' }, +] as const + +describe('versioned-proxy AdminOp builders', () => { + it('transferProxyAdmin', () => { + const op = vp.transferProxyAdmin(PROXY, NEW_ADMIN) + expect(op.to).toBe(PROXY) + expect(op.value).toBe(0n) + expect(op.data.slice(0, 10)).toBe(SELECTOR_TRANSFER) + const decoded = decodeFunctionData({ abi: ABI, data: op.data }) + expect(decoded.functionName).toBe('transferProxyAdmin') + expect(decoded.args).toEqual([NEW_ADMIN]) + }) + + it('acceptProxyAdmin (no args, selector only)', () => { + const op = vp.acceptProxyAdmin(PROXY) + expect(op.data).toBe(SELECTOR_ACCEPT) + expect(op.data.length).toBe(10) + const decoded = decodeFunctionData({ abi: ABI, data: op.data }) + expect(decoded.functionName).toBe('acceptProxyAdmin') + expect(decoded.args ?? []).toEqual([]) + }) + + it('registerVersion', () => { + const op = vp.registerVersion(PROXY, VERSION_BYTES32, NEW_IMPL) + expect(op.data.slice(0, 10)).toBe(SELECTOR_REGISTER) + const decoded = decodeFunctionData({ abi: ABI, data: op.data }) + expect(decoded.functionName).toBe('registerVersion') + expect(decoded.args).toEqual([VERSION_BYTES32, NEW_IMPL]) + }) + + it('setDefaultVersion', () => { + const op = vp.setDefaultVersion(PROXY, VERSION_BYTES32) + expect(op.data.slice(0, 10)).toBe(SELECTOR_SET_DEFAULT) + const decoded = decodeFunctionData({ abi: ABI, data: op.data }) + expect(decoded.functionName).toBe('setDefaultVersion') + expect(decoded.args).toEqual([VERSION_BYTES32]) + }) + + it('all builders target the proxy address with value 0n', () => { + const ops = [ + vp.transferProxyAdmin(PROXY, NEW_ADMIN), + vp.acceptProxyAdmin(PROXY), + vp.registerVersion(PROXY, VERSION_BYTES32, NEW_IMPL), + vp.setDefaultVersion(PROXY, VERSION_BYTES32), + ] + for (const op of ops) { + expect(op.to).toBe(PROXY) + expect(op.value).toBe(0n) + expect(op.description.length).toBeGreaterThan(0) + } + }) +}) From c3e3f7073ffdd4fb7b16c73c9c7137525a5e6ad0 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:44:40 +0200 Subject: [PATCH 07/15] feat(safe-tx): real Ledger signer (optional deps + dynamic import) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the stub with a working Ledger over USB implementation. Deps are declared as `optionalDependencies` so users who don't need Ledger don't have to install hundreds of MB of native bindings. src/signers/ledger.ts: - Dynamic imports @ledgerhq/hw-app-eth + @ledgerhq/hw-transport-node-hid - Falls back with an actionable "install" hint when deps missing - Falls back with "is the device unlocked / Ledger Live closed" hint when the USB transport can't be opened - toAccount-wrapped Signer with: * signTypedData via signEIP712Message (full struct on-device, requires Ledger Eth app v1.10+; bigints serialized as decimal strings to round-trip JSON.stringify cleanly) * signMessage via signPersonalMessage * signTransaction explicitly NOT implemented — the Safe execution path uses signTypedData; raw tx signing is a different flow package.json: - @ledgerhq/hw-app-eth ^6.39.0 + @ledgerhq/hw-transport-node-hid ^6.29.5 in optionalDependencies - bun install ignores failures here so the rest of the workspace installs cleanly even if a developer's platform can't build the native HID bindings test/unit/signers/factory.test.ts: - Updated the ledger assertion to match the new "requires optional deps" error path (since the deps aren't installed by default in CI, we always hit the dynamic-import catch) Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/safe-tx/package.json | 4 + packages/safe-tx/src/signers/ledger.ts | 89 ++++++++++++++++--- .../safe-tx/test/unit/signers/factory.test.ts | 10 ++- 3 files changed, 91 insertions(+), 12 deletions(-) diff --git a/packages/safe-tx/package.json b/packages/safe-tx/package.json index deb21c0..25c706d 100644 --- a/packages/safe-tx/package.json +++ b/packages/safe-tx/package.json @@ -25,6 +25,10 @@ "dependencies": { "commander": "^12.0.0" }, + "optionalDependencies": { + "@ledgerhq/hw-app-eth": "^6.39.0", + "@ledgerhq/hw-transport-node-hid": "^6.29.5" + }, "peerDependencies": { "viem": "^2.0.0" }, diff --git a/packages/safe-tx/src/signers/ledger.ts b/packages/safe-tx/src/signers/ledger.ts index 9e8b05e..366d57f 100644 --- a/packages/safe-tx/src/signers/ledger.ts +++ b/packages/safe-tx/src/signers/ledger.ts @@ -1,3 +1,5 @@ +import type { Hex } from 'viem' +import { toAccount } from 'viem/accounts' import type { Signer } from './types.js' export type LedgerSignerOptions = { @@ -6,16 +8,83 @@ export type LedgerSignerOptions = { } /** - * Stub. Hardware-wallet signing via @ledgerhq/hw-app-eth + - * @ledgerhq/hw-transport-node-hid. Recommended for production prod - * signers — key never leaves the device. + * Hardware-wallet signing via Ledger over USB. Uses dynamic imports of + * `@ledgerhq/hw-app-eth` + `@ledgerhq/hw-transport-node-hid` so the + * deps stay optional — install them only if you actually use this: * - * Implementation deferred until the CLI's mainnet path actually needs - * it. The stub keeps the strategy dispatched in the factory so - * downstream code can target the final API today. + * bun add @ledgerhq/hw-app-eth @ledgerhq/hw-transport-node-hid + * + * EIP-712 signing uses `signEIP712Message` (full JSON, displays the + * struct on-device). Requires Ledger Ethereum app v1.10+. Older app + * versions can be updated via Ledger Live. + * + * Pre-flight: device unlocked, Ethereum app open, Ledger Live closed + * (Live grabs the USB transport and blocks other clients). */ -export async function ledgerSigner(_opts: LedgerSignerOptions = {}): Promise { - throw new Error( - 'safe-tx: ledger signer not yet implemented. Install @ledgerhq/hw-app-eth + @ledgerhq/hw-transport-node-hid and wire the implementation.', - ) +export async function ledgerSigner(opts: LedgerSignerOptions = {}): Promise { + const derivationPath = opts.derivationPath ?? "44'/60'/0'/0/0" + + let TransportNodeHid: typeof import('@ledgerhq/hw-transport-node-hid').default + let Eth: typeof import('@ledgerhq/hw-app-eth').default + try { + const transportMod = await import('@ledgerhq/hw-transport-node-hid') + const ethMod = await import('@ledgerhq/hw-app-eth') + TransportNodeHid = transportMod.default + Eth = ethMod.default + } catch { + throw new Error( + 'safe-tx: ledger signer requires optional deps. Install: ' + + 'bun add @ledgerhq/hw-app-eth @ledgerhq/hw-transport-node-hid', + ) + } + + let transport: Awaited> + try { + transport = await TransportNodeHid.create() + } catch (err) { + const msg = err instanceof Error ? err.message : String(err) + throw new Error( + `safe-tx: cannot open USB transport to Ledger. Is the device connected, unlocked, and Ethereum app open? Close Ledger Live first. (${msg})`, + ) + } + + const eth = new Eth(transport) + const { address } = await eth.getAddress(derivationPath) + const account = address as `0x${string}` + + function packSig(r: string, s: string, v: number): Hex { + const vHex = v.toString(16).padStart(2, '0') + return (`0x${r}${s}${vHex}`) as Hex + } + + return toAccount({ + address: account, + + async signTypedData(typedData) { + // viem may pass bigints in the typed data — JSON.stringify needs + // help. Pass as decimal strings; the Ledger Eth app accepts that. + const json = JSON.stringify(typedData, (_k, v) => + typeof v === 'bigint' ? v.toString() : v, + ) + const sig = await eth.signEIP712Message(derivationPath, json) + return packSig(sig.r, sig.s, sig.v) + }, + + async signMessage({ message }) { + const raw = + typeof message === 'string' + ? Buffer.from(message, 'utf-8').toString('hex') + : (message.raw as string).startsWith('0x') + ? (message.raw as string).slice(2) + : Buffer.from(message.raw as string, 'utf-8').toString('hex') + const sig = await eth.signPersonalMessage(derivationPath, raw) + return packSig(sig.r, sig.s, sig.v) + }, + + async signTransaction(_transaction) { + throw new Error( + 'safe-tx ledger signer: signTransaction not implemented. The Safe execution path uses signTypedData; if you need raw tx signing for a different flow, file an issue.', + ) + }, + }) } diff --git a/packages/safe-tx/test/unit/signers/factory.test.ts b/packages/safe-tx/test/unit/signers/factory.test.ts index c500162..2dbbe4d 100644 --- a/packages/safe-tx/test/unit/signers/factory.test.ts +++ b/packages/safe-tx/test/unit/signers/factory.test.ts @@ -11,8 +11,14 @@ describe('getSigner factory', () => { expect(signer.address).toBe(TEST_ADDR) }) - it('rejects with "not yet implemented" for ledger strategy', async () => { - await expect(getSigner('ledger')).rejects.toThrow(/ledger.*not yet implemented/i) + it('rejects clearly when ledger optional deps are not installed', async () => { + // The signer dynamically imports @ledgerhq/* — if they're not in + // node_modules (default in this repo since they're optional), the + // signer should fail with an actionable install hint, not a cryptic + // "Cannot find module" stack trace. + await expect(getSigner('ledger')).rejects.toThrow( + /ledger signer requires optional deps|@ledgerhq/i, + ) }) it('rejects with "not yet implemented" for walletconnect strategy', async () => { From 71a11e23f6b6e88183a34edace88327c4c74bb0f Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:50:16 +0200 Subject: [PATCH 08/15] fix(safe-tx): ledger signer EIP712 type + transport timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues from the initial Ledger commit (c3e3f70): 1. signEIP712Message takes an EIP712Message *object*, not the JSON.stringify-ed string — the previous code threw TS2345 at typecheck. Round-trip through JSON only to coerce bigints to decimal strings (which the SDK requires) but pass the resulting object back to the SDK. 2. TransportNodeHid.create() can hang for the full vitest test timeout (30s) when no Ledger is plugged in and the @ledgerhq deps are installed. Wrap in a Promise.race with a 3s default timeout (configurable via opts.transportTimeoutMs) so the signer surfaces "cannot open USB transport" quickly instead of stalling. test/unit/signers/factory.test.ts: - Updated assertion to accept either failure path (deps missing OR USB transport timeout) — both are user-actionable. - Pass transportTimeoutMs: 1500 + a 5s test timeout so the test itself stays snappy in CI. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/safe-tx/src/signers/ledger.ts | 49 +++++++++++++------ .../safe-tx/test/unit/signers/factory.test.ts | 20 +++++--- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/packages/safe-tx/src/signers/ledger.ts b/packages/safe-tx/src/signers/ledger.ts index 366d57f..d275a6a 100644 --- a/packages/safe-tx/src/signers/ledger.ts +++ b/packages/safe-tx/src/signers/ledger.ts @@ -5,24 +5,27 @@ import type { Signer } from './types.js' export type LedgerSignerOptions = { /** BIP-44 path. Default: "44'/60'/0'/0/0" (first Ethereum account). */ derivationPath?: string + /** Max time to wait for USB transport open. Default: 3000ms. */ + transportTimeoutMs?: number } /** * Hardware-wallet signing via Ledger over USB. Uses dynamic imports of - * `@ledgerhq/hw-app-eth` + `@ledgerhq/hw-transport-node-hid` so the - * deps stay optional — install them only if you actually use this: + * `@ledgerhq/hw-app-eth` + `@ledgerhq/hw-transport-node-hid` (declared + * as `optionalDependencies`) so the heavy native bindings only need to + * resolve when this signer is actually selected. * - * bun add @ledgerhq/hw-app-eth @ledgerhq/hw-transport-node-hid - * - * EIP-712 signing uses `signEIP712Message` (full JSON, displays the - * struct on-device). Requires Ledger Ethereum app v1.10+. Older app - * versions can be updated via Ledger Live. + * EIP-712 signing uses `signEIP712Message` (full struct on-device, + * requires Ledger Ethereum app v1.10+). For Intuition mainnet + * (chain id 1155, not in Ledger's pre-shipped chain list), the device's + * Ethereum app must have "Blind signing" enabled in Settings. * * Pre-flight: device unlocked, Ethereum app open, Ledger Live closed * (Live grabs the USB transport and blocks other clients). */ export async function ledgerSigner(opts: LedgerSignerOptions = {}): Promise { const derivationPath = opts.derivationPath ?? "44'/60'/0'/0/0" + const transportTimeoutMs = opts.transportTimeoutMs ?? 3000 let TransportNodeHid: typeof import('@ledgerhq/hw-transport-node-hid').default let Eth: typeof import('@ledgerhq/hw-app-eth').default @@ -38,9 +41,24 @@ export async function ledgerSigner(opts: LedgerSignerOptions = {}): Promise> try { - transport = await TransportNodeHid.create() + transport = await Promise.race([ + TransportNodeHid.create(), + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error(`USB transport open timed out after ${transportTimeoutMs}ms`), + ), + transportTimeoutMs, + ), + ), + ]) } catch (err) { const msg = err instanceof Error ? err.message : String(err) throw new Error( @@ -61,12 +79,15 @@ export async function ledgerSigner(opts: LedgerSignerOptions = {}): Promise - typeof v === 'bigint' ? v.toString() : v, - ) - const sig = await eth.signEIP712Message(derivationPath, json) + // The Ledger SDK takes an EIP712Message *object* (not JSON string), + // and all numeric values must be decimal strings (no bigints). Round- + // trip through JSON to coerce bigints while preserving structure. + const eip712Message = JSON.parse( + JSON.stringify(typedData, (_k, v) => + typeof v === 'bigint' ? v.toString() : v, + ), + ) as Parameters[1] + const sig = await eth.signEIP712Message(derivationPath, eip712Message) return packSig(sig.r, sig.s, sig.v) }, diff --git a/packages/safe-tx/test/unit/signers/factory.test.ts b/packages/safe-tx/test/unit/signers/factory.test.ts index 2dbbe4d..525f251 100644 --- a/packages/safe-tx/test/unit/signers/factory.test.ts +++ b/packages/safe-tx/test/unit/signers/factory.test.ts @@ -11,15 +11,19 @@ describe('getSigner factory', () => { expect(signer.address).toBe(TEST_ADDR) }) - it('rejects clearly when ledger optional deps are not installed', async () => { - // The signer dynamically imports @ledgerhq/* — if they're not in - // node_modules (default in this repo since they're optional), the - // signer should fail with an actionable install hint, not a cryptic - // "Cannot find module" stack trace. - await expect(getSigner('ledger')).rejects.toThrow( - /ledger signer requires optional deps|@ledgerhq/i, + it('rejects with an actionable error when ledger has no device + no deps', async () => { + // Two valid failure paths depending on whether @ledgerhq/* optional + // deps were installed by bun: + // - deps missing -> "ledger signer requires optional deps" + // - deps installed but no device plugged -> "cannot open USB transport" + // Both are user-actionable and resolve quickly thanks to the 3s + // transport open timeout in the signer. + await expect( + getSigner('ledger', { ledger: { transportTimeoutMs: 1500 } }), + ).rejects.toThrow( + /ledger signer requires optional deps|cannot open USB transport/i, ) - }) + }, 5000) it('rejects with "not yet implemented" for walletconnect strategy', async () => { await expect(getSigner('walletconnect')).rejects.toThrow(/walletconnect.*not yet implemented/i) From c806234fd87d3e2a918f01719ccd887ea4990a89 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 12:52:42 +0200 Subject: [PATCH 09/15] =?UTF-8?q?refactor(webapp):=20tighten=20admin=20&?= =?UTF-8?q?=20sponsoring=20panels=20=E2=80=94=20copy=20+=20density?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UpgradeAuthorityPanel: collapse "grant both roles" rotation flow into a

    block (advanced), promote standalone Grant Role 1 to btn-primary, drop ✓ glyphs in step states (Tailwind colors instead), strip verbose intros and trailing role-1 explainer. AdminsPanel / AdminsTab: trim role-2 description and tab intro to one line each. Inline the "prefer a Safe" note next to the Grant Role 2 header. SponsoringTab: replace the 4 Stat cards with a compact ClaimLimitsStrip (single inline row), kills the orphaned 4th cell. Fund + Reclaim now share an equal-height grid (h-full + mt-auto) instead of stacking. FundPoolPanel / ReclaimFromPoolPanel: short one-line intros, remove "Counterpart to Fund pool" eyebrow, drop redundant pool-balance hint (already in PoolHealthBadge), drop unused poolBalance prop. HistoryTab / MetricsTab: remove implementation-detail paragraphs that weren't load-bearing for end users. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../webapp/src/components/AdminsPanel.tsx | 13 +- packages/webapp/src/components/AdminsTab.tsx | 6 +- .../webapp/src/components/FundPoolPanel.tsx | 34 +++--- packages/webapp/src/components/HistoryTab.tsx | 5 +- packages/webapp/src/components/MetricsTab.tsx | 11 -- .../src/components/ReclaimFromPoolPanel.tsx | 66 +++++------ .../webapp/src/components/SponsoringTab.tsx | 85 ++++++++------ .../src/components/UpgradeAuthorityPanel.tsx | 111 ++++++++---------- 8 files changed, 155 insertions(+), 176 deletions(-) diff --git a/packages/webapp/src/components/AdminsPanel.tsx b/packages/webapp/src/components/AdminsPanel.tsx index 2cae0bb..47b576b 100644 --- a/packages/webapp/src/components/AdminsPanel.tsx +++ b/packages/webapp/src/components/AdminsPanel.tsx @@ -143,10 +143,8 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) {

    - Controls money flow — withdraw accumulated fees, change fee settings, - add / revoke other fee admins, and on sponsored proxies fundPool / - reclaim / setClaimLimits. Cannot register new - implementation versions. List is reconstructed from on-chain events. + Controls fees, withdrawals and (sponsored) pool funding. Cannot + register new implementations.

    {isLoading && admins.length === 0 && ( @@ -228,13 +226,12 @@ export function AdminsPanel({ proxy, connectedAccount }: Props) { {canInteract ? (
    -
    +
    Grant Role 2 to a new address
    -
    - Grants fee admin rights. Use a Safe or multisig address for - production deployments — granting to an EOA concentrates trust. +
    + Prefer a Safe — granting to an EOA concentrates trust
    diff --git a/packages/webapp/src/components/AdminsTab.tsx b/packages/webapp/src/components/AdminsTab.tsx index d5d4d1c..53352c7 100644 --- a/packages/webapp/src/components/AdminsTab.tsx +++ b/packages/webapp/src/components/AdminsTab.tsx @@ -28,10 +28,8 @@ export function AdminsTab({ return (

    - Two independent admin roles — disjoint by design so that a compromise - of one cannot be leveraged into the other. Most setups use a single - Safe for both; splitting is an option if your dev team and ops team - are distinct. + Two independent roles. A single Safe can hold both, or split them + between dev and ops.

    +

    Fund the pool

    - Top up the shared sponsorship pool with TRUST from your wallet. - Any user interacting with this proxy will draw from the pool - transparently (bounded by the per-user rate limits). + Permissionless — anyone can top up the shared pool.

    @@ -57,20 +55,22 @@ export function FundPoolPanel({ proxy, onDone }: Props) { /> - +
    + - {error && ( -

    - {error.message.split('\n')[0]} -

    - )} + {error && ( +

    + {error.message.split('\n')[0]} +

    + )} +
    ) } diff --git a/packages/webapp/src/components/HistoryTab.tsx b/packages/webapp/src/components/HistoryTab.tsx index 76947b9..84be726 100644 --- a/packages/webapp/src/components/HistoryTab.tsx +++ b/packages/webapp/src/components/HistoryTab.tsx @@ -127,10 +127,7 @@ export function HistoryTab({ proxy, isAdmin, channel }: Props) {

    History

    Chronological log of every public event on this proxy — pool - contributions, refunds, and fee withdrawals. Reconstructed - client-side from on-chain events; everyone sees the same data. - Admins get a one-click Refund on top-ups (amount + donor - pre-filled from the event). + contributions, refunds, and fee withdrawals.

    {isSponsored && (
    diff --git a/packages/webapp/src/components/MetricsTab.tsx b/packages/webapp/src/components/MetricsTab.tsx index 2a5dadf..7e3bed6 100644 --- a/packages/webapp/src/components/MetricsTab.tsx +++ b/packages/webapp/src/components/MetricsTab.tsx @@ -67,12 +67,6 @@ export function MetricsTab({

    On-chain metrics

    -

    - Aggregate counters read from the proxy itself. Every write-path - call updates these and emits a{' '} - MetricsUpdated event — - single-source-of-truth for dashboards and off-chain indexers. -

    {unsupported && ( @@ -130,11 +124,6 @@ export function MetricsTab({

    Sponsored activity

    -

    - Subset of the metrics above that went through the sponsor pool - (fully or partially covered by{' '} - sponsorPool). -

    void } -export function ReclaimFromPoolPanel({ proxy, poolBalance, onDone }: Props) { +export function ReclaimFromPoolPanel({ proxy, onDone }: Props) { const [amount, setAmount] = useState('') const [to, setTo] = useState('') const { reclaim, hash, isPending, error, reset } = useReclaimFromPool(proxy) @@ -56,15 +55,11 @@ export function ReclaimFromPoolPanel({ proxy, poolBalance, onDone }: Props) { } return ( -
    +
    -
    - Counterpart to Fund pool -
    -

    Reclaim from pool

    -

    - Withdraw TRUST you previously funded but that users haven't - spent yet. +

    Reclaim from pool

    +

    + Withdraw unspent TRUST you previously funded.

    @@ -89,41 +84,38 @@ export function ReclaimFromPoolPanel({ proxy, poolBalance, onDone }: Props) { placeholder="1.0" className="input" /> - {poolBalance !== undefined && ( -
    - Pool balance: {formatEther(poolBalance)} TRUST -
    - )} -
    - - {safe && ( +
    +
    - )} -
    + {safe && ( + + )} +
    - + - {error && ( -

    - {error.message.split('\n')[0]} -

    - )} + {error && ( +

    + {error.message.split('\n')[0]} +

    + )} +
    ) } diff --git a/packages/webapp/src/components/SponsoringTab.tsx b/packages/webapp/src/components/SponsoringTab.tsx index c54980b..66e6973 100644 --- a/packages/webapp/src/components/SponsoringTab.tsx +++ b/packages/webapp/src/components/SponsoringTab.tsx @@ -4,13 +4,13 @@ import { useClaimLimits, usePoolBurnRate, useSponsorPool, + type ClaimLimits, } from '../hooks/useSponsoredProxy' import { formatWindow } from '../lib/format' import { ClaimLimitsPanel } from './ClaimLimitsPanel' import { FundPoolPanel } from './FundPoolPanel' import { PoolHealthBadge } from './PoolHealthBadge' import { ReclaimFromPoolPanel } from './ReclaimFromPoolPanel' -import { Stat } from './Stat' interface Props { proxy: Address @@ -29,50 +29,63 @@ export function SponsoringTab({ proxy, isAdmin }: Props) { } return ( -
    +
    -
    - - - - -
    - - {/* Permissionless — anyone can contribute to the pool. */} - + {isAdmin ? ( <> - + {/* Fund: permissionless. Reclaim: admin-only. Side-by-side, equal height. */} +
    + + +
    ) : ( -

    - Reclaiming pool balance and tuning claim limits is restricted to - whitelisted admins of this proxy. -

    + <> + +

    + Reclaiming pool balance and tuning claim limits is restricted to + whitelisted admins of this proxy. +

    + )}
    ) } + +function ClaimLimitsStrip({ limits }: { limits: ClaimLimits | undefined }) { + const items: { label: string; value: string }[] = [ + { + label: 'Max / tx', + value: limits ? `${formatEther(limits.maxClaimPerTx)} TRUST` : '—', + }, + { + label: 'Max calls / user', + value: limits ? limits.maxClaimsPerWindow.toString() : '—', + }, + { + label: 'Max TRUST / user', + value: limits ? `${formatEther(limits.maxClaimVolumePerWindow)} TRUST` : '—', + }, + { + label: 'Window', + value: limits ? formatWindow(limits.claimWindowSeconds) : '—', + }, + ] + return ( +
    + + Claim limits + + {items.map((it) => ( + + {it.label} + {it.value} + + ))} +
    + ) +} diff --git a/packages/webapp/src/components/UpgradeAuthorityPanel.tsx b/packages/webapp/src/components/UpgradeAuthorityPanel.tsx index 9b2f395..472f3e8 100644 --- a/packages/webapp/src/components/UpgradeAuthorityPanel.tsx +++ b/packages/webapp/src/components/UpgradeAuthorityPanel.tsx @@ -250,10 +250,15 @@ export function UpgradeAuthorityPanel({ )} {(isYou || proxyAdminSafe) && ( -
    - +
    +
    + + + 2-step · target then signs acceptProxyAdmin() + +
    {transferPending ? 'Sign…' : transferReceipt.isLoading ? 'Mining…' : hasPending - ? 'Grant (replace pending)' + ? 'Replace pending' : 'Grant'} )} @@ -310,25 +315,30 @@ export function UpgradeAuthorityPanel({ {transferError.message.split('\n')[0]}

    )} -

    - Grants Role 1 only — the target must then call{' '} - acceptProxyAdmin(). - Fee admin rights (Role 2) are untouched. Use the combined grant - below if you hold both roles. -

    )} - {hasBothRoles && } + {hasBothRoles && ( +
    + + + + Advanced + + + Grant both roles to a single address + + + expand + collapse + +
    + +
    +
    + )} - -

    - Role 1 controls which logic the proxy delegates to — - register new implementations, change default version, rename. It{' '} - cannot touch fees, withdrawals, or the sponsor - pool; those are Role 2 below. -

    ) } @@ -355,23 +365,20 @@ function RotateBothRolesForm({ } })() + const stepDone = (s: 'grant' | 'transfer' | 'accept') => { + if (s === 'grant') + return rotation.grantConfirmed || rotation.stage === 'transfer' || rotation.stage === 'done' + if (s === 'transfer') return rotation.stage === 'done' + return false + } + return ( -
    -
    - - Convenience - - - Grant both roles to a single address - -
    +

    - You currently hold both roles. This runs{' '} - setWhitelistedAdmin(new, true){' '} - then transferProxyAdmin(new){' '} - back-to-back (2 signatures). After the new admin calls{' '} - acceptProxyAdmin(), they - can revoke you as fee admin from their wallet. + Two signatures: setWhitelistedAdmin{' '} + then transferProxyAdmin. The + new admin then calls{' '} + acceptProxyAdmin().

    {rotation.stage !== 'complete' && (
    @@ -395,38 +402,24 @@ function RotateBothRolesForm({
    )} {rotation.stage !== 'idle' && rotation.stage !== 'complete' && ( -
      -
    1. - {rotation.grantConfirmed ? '✓ ' : ''}Grant fee admin to new - address (immediate) +
        +
      1. + Grant fee admin (immediate)
      2. -
      3. - {rotation.stage === 'done' ? '✓ ' : ''}Initiate proxyAdmin - transfer (pending until accepted) +
      4. + Transfer proxyAdmin (pending)
      5. -
      6. +
      7. New admin calls{' '} - acceptProxyAdmin(){' '} - (auto-detected) -
      8. -
      9. - (optional) Revoke your old fee admin rights from the new - admin's wallet + acceptProxyAdmin()
      )} {rotation.stage === 'complete' && (
      -

      - ✓ Rotation complete — the new address is now proxyAdmin + fee - admin. You can optionally revoke yourself as fee admin from the - new admin's wallet. +

      + Rotation complete. Optionally revoke your fee admin rights from + the new admin's wallet.

      -
      + {proxySucceeded && newProxyAddress && ( +
      + + Open detail → + + +
      + )}
    - )} +
    + ) + } + + return ( +
    +
    +

    + Deploy a fee proxy +

    +

    + Configure fees and admins. The proxy is upgradeable via version registry. +

    +
    {!factory && (
    From 6ad22eb7ffdfdf2503f80c74b609b7f510ac7302 Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 15:25:13 +0200 Subject: [PATCH 14/15] chore: gitignore tsbuildinfo + vite.config build artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stops three classes of build artifacts from polluting future commits: - *.tsbuildinfo (incremental tsc cache, regenerated on every build) - vite.config.d.ts + vite.config.js (compiled config emitted by some Vite + tsc combinations) Removes the two tracked tsbuildinfo files via `git rm --cached` so the working tree stops showing them as modified after every build. Pre-existing bun.lock + package.json changes from the user's `bun add @trezor/connect` are deliberately not bundled here — those belong with the Trezor signer story. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 3 +++ packages/webapp/tsconfig.node.tsbuildinfo | 1 - packages/webapp/tsconfig.tsbuildinfo | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 packages/webapp/tsconfig.node.tsbuildinfo delete mode 100644 packages/webapp/tsconfig.tsbuildinfo diff --git a/.gitignore b/.gitignore index 4e60f8f..0e7a796 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ **/typechain-types/ **/dist/ **/.next/ +**/*.tsbuildinfo +**/vite.config.d.ts +**/vite.config.js # Ignores development broadcast logs !/broadcast diff --git a/packages/webapp/tsconfig.node.tsbuildinfo b/packages/webapp/tsconfig.node.tsbuildinfo deleted file mode 100644 index 085d3d8..0000000 --- a/packages/webapp/tsconfig.node.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/compatibility/index.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/globals.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/web-globals/events.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/header.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/file.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/client.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/api.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/util.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","../../node_modules/.bun/undici-types@6.21.0/node_modules/undici-types/index.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/assert.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/assert/strict.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/async_hooks.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/buffer.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/child_process.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/cluster.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/console.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/constants.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/crypto.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/dgram.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/dns.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/dns/promises.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/domain.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/events.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/fs.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/fs/promises.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/http.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/http2.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/https.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/inspector.generated.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/module.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/net.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/os.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/path.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/perf_hooks.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/process.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/punycode.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/querystring.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/readline.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/readline/promises.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/repl.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/sea.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/stream.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/stream/promises.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/stream/consumers.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/stream/web.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/string_decoder.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/test.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/timers.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/timers/promises.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/tls.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/trace_events.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/tty.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/url.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/util.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/v8.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/vm.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/wasi.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/worker_threads.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/zlib.d.ts","../../node_modules/.bun/@types+node@20.19.39/node_modules/@types/node/index.d.ts","../../node_modules/.bun/@types+estree@1.0.8/node_modules/@types/estree/index.d.ts","../../node_modules/.bun/rollup@4.60.1/node_modules/rollup/dist/rollup.d.ts","../../node_modules/.bun/rollup@4.60.1/node_modules/rollup/dist/parseAst.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/types/hmrPayload.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/types/customEvent.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/types/hot.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/dist/node/types.d-aGj9QkWt.d.ts","../../node_modules/.bun/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","../../node_modules/.bun/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/previous-map.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/input.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/css-syntax-error.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/declaration.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/root.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/warning.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/lazy-result.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/no-work-result.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/processor.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/result.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/document.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/rule.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/node.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/comment.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/container.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/at-rule.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/list.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/postcss.d.ts","../../node_modules/.bun/postcss@8.5.10/node_modules/postcss/lib/postcss.d.mts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/dist/node/runtime.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/types/importGlob.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/types/metadata.d.ts","../../node_modules/.bun/vite@5.4.21+f61fc156e9fb716d/node_modules/vite/dist/node/index.d.ts","../../node_modules/.bun/@babel+types@7.29.0/node_modules/@babel/types/lib/index.d.ts","../../node_modules/.bun/@types+babel__generator@7.27.0/node_modules/@types/babel__generator/index.d.ts","../../node_modules/.bun/@babel+parser@7.29.2/node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/.bun/@types+babel__template@7.4.4/node_modules/@types/babel__template/index.d.ts","../../node_modules/.bun/@types+babel__traverse@7.28.0/node_modules/@types/babel__traverse/index.d.ts","../../node_modules/.bun/@types+babel__core@7.20.5/node_modules/@types/babel__core/index.d.ts","../../node_modules/.bun/@vitejs+plugin-react@4.7.0+885654ef1d50a9ee/node_modules/@vitejs/plugin-react/dist/index.d.ts","./vite.config.ts","../../node_modules/.bun/@types+react@18.3.28/node_modules/@types/react/global.d.ts","../../node_modules/.bun/csstype@3.2.3/node_modules/csstype/index.d.ts","../../node_modules/.bun/@types+prop-types@15.7.15/node_modules/@types/prop-types/index.d.ts","../../node_modules/.bun/@types+react@18.3.28/node_modules/@types/react/index.d.ts","../../node_modules/.bun/@types+react-dom@18.3.7+9b6d2b90343c6348/node_modules/@types/react-dom/index.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/constants.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/Layers.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Vector2.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Matrix3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/BufferAttribute.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/InterleavedBuffer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/InterleavedBufferAttribute.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Quaternion.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Euler.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Matrix4.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Vector4.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/cameras/Camera.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/ColorManagement.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Color.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Cylindrical.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Spherical.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Vector3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Bone.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Interpolant.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/interpolants/CubicInterpolant.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/interpolants/DiscreteInterpolant.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/interpolants/LinearInterpolant.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/KeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/AnimationClip.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/core/Curve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/core/CurvePath.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/core/Path.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/core/Shape.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Line3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Sphere.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Plane.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Triangle.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Box3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/EventDispatcher.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/GLBufferAttribute.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/BufferGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Group.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/CompressedTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/CubeTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/Source.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/Texture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/LineBasicMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/LineDashedMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshBasicMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshDepthMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshDistanceMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshLambertMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshMatcapMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshNormalMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshPhongMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshStandardMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshPhysicalMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/MeshToonMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/PointsMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/Uniform.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/UniformsGroup.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/shaders/UniformsLib.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/ShaderMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/RawShaderMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/ShadowMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/SpriteMaterial.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/Materials.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Sprite.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Frustum.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/DepthTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/RenderTarget.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/WebGLRenderTarget.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/LightShadow.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/Light.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/scenes/Fog.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/scenes/FogExp2.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/scenes/Scene.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Box2.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/types.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/Data3DTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/DataArrayTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLCapabilities.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLExtensions.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLProperties.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLState.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLTextures.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLUniforms.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLProgram.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLInfo.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLRenderLists.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLObjects.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLShadowMap.d.ts","../../node_modules/.bun/@types+webxr@0.5.24/node_modules/@types/webxr/index.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/cameras/PerspectiveCamera.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/cameras/ArrayCamera.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Mesh.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webxr/WebXRController.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webxr/WebXRManager.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/WebGLRenderer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLAttributes.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLBindingStates.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLClipping.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLCubeMaps.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLLights.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLPrograms.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/materials/Material.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/DataTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Skeleton.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Ray.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/Raycaster.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/Object3D.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/AnimationObjectGroup.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/AnimationMixer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/AnimationAction.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/AnimationUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/PropertyBinding.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/PropertyMixer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/tracks/BooleanKeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/tracks/ColorKeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/tracks/NumberKeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/tracks/QuaternionKeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/tracks/StringKeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/animation/tracks/VectorKeyframeTrack.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/audio/AudioContext.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/audio/AudioListener.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/audio/Audio.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/audio/AudioAnalyser.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/audio/PositionalAudio.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/WebGLCubeRenderTarget.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/cameras/CubeCamera.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/cameras/OrthographicCamera.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/cameras/StereoCamera.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/Clock.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/InstancedBufferAttribute.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/InstancedBufferGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/core/InstancedInterleavedBuffer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/Controls.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/core/ShapePath.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/EllipseCurve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/ArcCurve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/CatmullRomCurve3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/CubicBezierCurve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/CubicBezierCurve3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/LineCurve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/LineCurve3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/QuadraticBezierCurve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/QuadraticBezierCurve3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/SplineCurve.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/curves/Curves.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/DataUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/ImageUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/PMREMGenerator.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/ShapeUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/extras/TextureUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/BoxGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/CapsuleGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/CircleGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/CylinderGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/ConeGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/PolyhedronGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/DodecahedronGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/EdgesGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/ExtrudeGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/IcosahedronGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/LatheGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/OctahedronGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/PlaneGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/RingGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/ShapeGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/SphereGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/TetrahedronGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/TorusGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/TorusKnotGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/TubeGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/WireframeGeometry.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/geometries/Geometries.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Line.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/ArrowHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/LineSegments.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/AxesHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/Box3Helper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/BoxHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/CameraHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/DirectionalLightShadow.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/DirectionalLight.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/DirectionalLightHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/GridHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/HemisphereLight.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/HemisphereLightHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/PlaneHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/PointLightShadow.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/PointLight.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/PointLightHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/PolarGridHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/SkinnedMesh.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/SkeletonHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/helpers/SpotLightHelper.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/AmbientLight.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/SphericalHarmonics3.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/LightProbe.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/RectAreaLight.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/SpotLightShadow.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/lights/SpotLight.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/LoadingManager.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/Loader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/AnimationLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/AudioLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/BufferGeometryLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/Cache.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/CompressedTextureLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/CubeTextureLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/DataTextureLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/FileLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/ImageBitmapLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/ImageLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/LoaderUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/MaterialLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/ObjectLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/loaders/TextureLoader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/interpolants/QuaternionLinearInterpolant.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/MathUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/math/Matrix2.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/BatchedMesh.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/InstancedMesh.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/LineLoop.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/LOD.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/objects/Points.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/shaders/ShaderChunk.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/shaders/ShaderLib.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/shaders/UniformsUtils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLBufferRenderer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLCubeUVMaps.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLGeometries.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLIndexedBufferRenderer.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webgl/WebGLShader.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/WebGL3DRenderTarget.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/WebGLArrayRenderTarget.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/renderers/webxr/WebXRDepthSensing.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/CanvasTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/CompressedArrayTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/CompressedCubeTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/FramebufferTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/textures/VideoTexture.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/Three.Legacy.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/utils.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/src/Three.d.ts","../../node_modules/.bun/@types+three@0.169.0/node_modules/@types/three/index.d.ts"],"fileIdsList":[[55,101,182],[55,101],[55,101,182,183,184,185,186],[55,101,182,184],[55,98,101],[55,100,101],[101],[55,101,106,134],[55,101,102,107,112,120,131,142],[55,101,102,103,112,120],[50,51,52,55,101],[55,101,104,143],[55,101,105,106,113,121],[55,101,106,131,139],[55,101,107,109,112,120],[55,100,101,108],[55,101,109,110],[55,101,111,112],[55,100,101,112],[55,101,112,113,114,131,142],[55,101,112,113,114,127,131,134],[55,101,109,112,115,120,131,142],[55,101,112,113,115,116,120,131,139,142],[55,101,115,117,131,139,142],[53,54,55,56,57,58,59,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148],[55,101,112,118],[55,101,119,142,147],[55,101,109,112,120,131],[55,101,121],[55,101,122],[55,100,101,123],[55,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148],[55,101,125],[55,101,126],[55,101,112,127,128],[55,101,127,129,143,145],[55,101,112,131,132,134],[55,101,133,134],[55,101,131,132],[55,101,134],[55,101,135],[55,98,101,131,136],[55,101,112,137,138],[55,101,137,138],[55,101,106,120,131,139],[55,101,140],[55,101,120,141],[55,101,115,126,142],[55,101,106,143],[55,101,131,144],[55,101,119,145],[55,101,146],[55,96,101],[55,96,101,112,114,123,131,134,142,145,147],[55,101,131,148],[55,101,193],[55,101,190,191,192],[55,101,436],[55,101,235,260,261],[55,101,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,249,250,251,256,257,258,259,260,261,262,263,264,265,266,267,269,270,271,272,273,274,275,276,277,278,279,280,281,282,284,285,286,287,288,289,290,291,292,293,294,295,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,339,340,341,342,343,344,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435],[55,101,195,218,301,303],[55,101,195,211,212,217],[55,101,195,218,228,301,302,304],[55,101,218],[55,101,195,213,214,215,216],[55,101,217],[55,101,195,217],[55,101,301,314,315],[55,101,316],[55,101,301,314],[55,101,315,316],[55,101,284],[55,101,195,196,204,205,211,301],[55,101,195,289,301,319],[55,101,206,301],[55,101,197,206,301],[55,101,206,284],[55,101,195,198,204],[55,101,197,199,201,202,204,211,224,227,228,229],[55,101,199],[55,101,230],[55,101,199,200],[55,101,195,199,201],[55,101,198,199,200,204],[55,101,196,198,202,203,204,206,211,218,222,228,230,231,234,235,266,289,296,298,300],[55,101,196,197,206,211,287,299,301],[55,101,195,205,228,235,259],[55,101,195,228,249],[55,101,228,301],[55,101,233,235,261,266,289],[55,101,197],[55,101,195,235],[55,101,197,211],[55,101,197,211,219],[55,101,197,220],[55,101,197,221],[55,101,197,208,221,222],[55,101,329],[55,101,211,219],[55,101,197,219],[55,101,329,330,331,332,333,334,335,336,337,338],[55,101,348],[55,101,350],[55,101,197,211,219,222,230],[55,101,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365],[55,101,197,230],[55,101,222,230],[55,101,211,219,230],[55,101,208,211,286,301,367],[55,101,208,369],[55,101,208,227,369],[55,101,208,230,236,301,369],[55,101,204,206,208,369],[55,101,204,208,301,367,375],[55,101,208,230,236,369],[55,101,204,208,238,301,378],[55,101,225,369],[55,101,204,208,301,382],[55,101,204,212,301,369,385],[55,101,204,208,263,301,369],[55,101,208,263],[55,101,208,211,263,301,374],[55,101,262,321],[55,101,208,211,263],[55,101,208,262,301],[55,101,263,389],[55,101,197,204,205,206,258,261,263,301],[55,101,208,263,381],[55,101,262,263,284],[55,101,208,211,235,263,301,392],[55,101,262,284],[55,101,218,394,395],[55,101,394,395],[55,101,230,325,394,395],[55,101,232,394,395],[55,101,233,394,395],[55,101,297,394,395],[55,101,394],[55,101,395],[55,101,235,296,394,395],[55,101,218,230,234,235,296,301,325,394,395],[55,101,235,394,395],[55,101,208,235,296],[55,101,236],[55,101,195,206,208,225,228,230,231,266,289,295,301,436],[55,101,236,237,238,239,240,241,242,243,244,245,246,247,248,252,253,254,255,296],[55,101,195,203,208,235,296],[55,101,195,235,296],[55,101,211,235,296],[55,101,195,197,203,208,235,296],[55,101,195,197,208,235,296],[55,101,195,197,235,296],[55,101,197,208,235,245],[55,101,252],[55,101,195,197,198,204,205,211,250,251,296,301],[55,101,208,296],[55,101,199,204,211,224,225,226,301],[55,101,195,198,199,201,207,211],[55,101,195,208,211],[55,101,211],[55,101,202,204,211],[55,101,195,204,211,224,225,227,257,301],[55,101,204,211],[55,101,202],[55,101,197,204,211],[55,101,195,198,202,203,211],[55,101,198,204,211,223,224,227],[55,101,199,201,203,204,211],[55,101,204,211,224,225,227],[55,101,204,211,225,227],[55,101,197,199,201,205,211,225,227],[55,101,198,199],[55,101,198,199,201,202,203,204,206,208,209,210],[55,101,199,202,204],[55,101,213],[55,101,204,206,208,224,227,230,286,296],[55,101,301],[55,101,199,204,208,224,227,230,286,296,297,301,324],[55,101,230,296,301],[55,101,230,296,301,367],[55,101,211,230,296,301],[55,101,204,212,297],[55,101,195,204,211,224,227,230,286,296,298,301],[55,101,197,230,256,301],[55,101,260,261,269],[55,101,260,261,270],[55,101,233,235,260,261,289],[55,101,235,260],[55,101,195,197,199,205,206,208,211,225,227,230,235,261,266,267,269,270,271,272,273,274,278,279,280,282,288,296,301],[55,101,251],[55,101,197,198,208],[55,101,250,251],[55,101,199,201,229],[55,101,199,230,278,290,296,301],[55,101,272,279],[55,101,195],[55,101,206,225,273,296],[55,101,289],[55,101,235,289],[55,101,199,230,279,290,301],[55,101,278],[55,101,272],[55,101,277,289],[55,101,195,251,263,266,271,272,278,289,291,292,293,294,296,301],[55,101,206,230,231,266,273,278,296,301],[55,101,195,206,263,266,271,281,289],[55,101,195,205,261,296],[55,101,271,272,273,274,275,279],[55,101,276,278],[55,101,195,272],[55,101,211,231,301],[55,101,235,286,288,289],[55,101,205,228,235,283,284,285,286,287,289],[55,101,208],[55,101,203,208,233,235,264,265,296,301],[55,101,195,232],[55,101,195,199,235],[55,101,195,235,268],[55,101,195,197,198,228,232,233,234],[55,101,181,187],[55,101,173],[55,101,171,173],[55,101,162,170,171,172,174,176],[55,101,160],[55,101,163,168,173,176],[55,101,159,176],[55,101,163,164,167,168,169,176],[55,101,163,164,165,167,168,176],[55,101,160,161,162,163,164,168,169,170,172,173,174,176],[55,101,176],[55,101,158,160,161,162,163,164,165,167,168,169,170,171,172,173,174,175],[55,101,158,176],[55,101,163,165,166,168,169,176],[55,101,167,176],[55,101,168,169,173,176],[55,101,161,171],[55,101,151,180],[55,101,150,151],[55,68,72,101,142],[55,68,101,131,142],[55,63,101],[55,65,68,101,139,142],[55,101,120,139],[55,101,149],[55,63,101,149],[55,65,68,101,120,142],[55,60,61,64,67,101,112,131,142],[55,68,75,101],[55,60,66,101],[55,68,89,90,101],[55,64,68,101,134,142,149],[55,89,101,149],[55,62,63,101,149],[55,68,101],[55,62,63,64,65,66,67,68,69,70,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,90,91,92,93,94,95,101],[55,68,83,101],[55,68,75,76,101],[55,66,68,76,77,101],[55,67,101],[55,60,63,68,101],[55,68,72,76,77,101],[55,72,101],[55,66,68,71,101,142],[55,60,65,68,75,101],[55,101,131],[55,63,68,89,101,147,149],[55,101,112,113,115,116,117,120,131,139,142,148,149,151,152,153,154,155,156,157,177,178,179,180],[55,101,153,154,155,156],[55,101,153,154,155],[55,101,153],[55,101,154],[55,101,151],[55,101,122,181,188]],"fileInfos":[{"version":"a7297ff837fcdf174a9524925966429eb8e5feecc2cc55cc06574e6b092c1eaa","impliedFormat":1},{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"70521b6ab0dcba37539e5303104f29b721bfb2940b2776da4cc818c07e1fefc1","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"ba481bca06f37d3f2c137ce343c7d5937029b2468f8e26111f3c9d9963d6568d","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d9ef24f9a22a88e3e9b3b3d8c40ab1ddb0853f1bfbd5c843c37800138437b61","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"929b4c540b70efdaee3390e40d123889e3410c022d49edeb81b36acd54f28b4e","impliedFormat":1},{"version":"6401927b62c7d8e62eb9af3b2ac6f5597e31f185b2052a0e891f8b0b1f04eef9","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"f8edab1fd539c4bc67932e1d0a37588528febc37ad8ee555d8a28aae30a7b770","impliedFormat":1},{"version":"62dbb9afbfbe0f14c68963c21034da7cd82fc752c8191f224dbbafaf470347e1","impliedFormat":1},{"version":"38ecaace40b2b571363e63992f1cb8e7973588df81b342c6f6ebf71fbffff906","impliedFormat":1},{"version":"383b91398f3ad5520d74661409ed1026e553a7ebc607028a8435385ddc8aa0e6","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"eaa2e9af2d32f0586ef50ff5621d3ff94d1e0033639d6b8c84b687bbefbf065f","impliedFormat":1},{"version":"860e2841ceb976416594ea4b428fbfe94a31418f172e27df7926072029034ac3","impliedFormat":1},{"version":"3283decbf4f544bc802a865b08d4d720383bb6ce6e4ed60e013bb40c6bbb6b90","impliedFormat":1},{"version":"f2e47827db13f95a20139b9be29e58b728e8e18f3f86289fa1e99589313e96f5","impliedFormat":1},{"version":"bd8d46dc9e5cfff76b40e8b418ef1e55848366c0ded87cdc06b5a43a9f5e9199","impliedFormat":1},{"version":"55b3a57d0a4e8f6e04b96cb3b9040e3f1bedfba796e072b374110fc832ca706c","impliedFormat":1},{"version":"3a5edc4c3a85102031e36f88ac0149a3c1599c3acabd3a8d62abdc767c96d468","impliedFormat":1},{"version":"5fa0dc3c25e77dc8488194dde96c862fb6df566c6bfc747b4dc8328c8551dc4a","impliedFormat":1},{"version":"e00f958278e17b33bea65dc9954e67b8cab7c47894fbf04afd7465e56283ddb8","impliedFormat":1},{"version":"926b74b0bfae3c43ea8cb9110f4036bbb48c6937593a82a2a29af0d7b027a292","impliedFormat":1},{"version":"3655183a607c5605e52f9d2acb9afcefd8140cfcc21d25345608a038bb4dd93b","impliedFormat":1},{"version":"a32f3a86595a70182a2801759b9c0287c16e8887d9a1c3b3bd19845fe185aae1","impliedFormat":1},{"version":"f5defd0791f7cb141bcc82dcca4bf52f90fcfe43d318ce818bb0a05e21aa8e68","impliedFormat":1},{"version":"85b97ce0065e4c3bde88ef39bbd071e8d7c650cd4d1c1126263ff159c30367fb","impliedFormat":1},{"version":"a26b1facfea6b62ce9ec55106d84fefd1300bdda27ba5ee2bd5ebb676135f644","impliedFormat":1},{"version":"037b8a322e4d96a7b4e21bf1ccc695d3e5b8758dcccec2427d1fcbdbf9cb6791","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"55245877be1a89483a0e3dc347a17652c034f44b10c3018a62ac8766b7401918","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"029d278301e67ff530caefca0992749bf220d0b3b61a7c604f06bdff3c014d85","impliedFormat":1},{"version":"efdb613729ff9021f37f591ac75def5696e56be59cc87224e94cd9240b85cde4","impliedFormat":1},{"version":"a721cceaeb9c8599a74b01fb3ed04e9f73db4f715e0e358996cce831f4992f30","impliedFormat":1},{"version":"6362d52572d95519061db0dfd4a4a570503e8e36ac0c4ac9502fa173f70fb9e8","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"88f9f1cfe56e8e4102031351e9a475378348e65b1515f530fc0fa8e661363f07","impliedFormat":1},{"version":"f590da8ae6e0ce1cf67566380658f5495a8929ad55878b405734e5f211b876ba","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"e2677634fe27e87348825bb041651e22d50a613e2fdf6a4a3ade971d71bac37e","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"8c0bcd6c6b67b4b503c11e91a1fb91522ed585900eab2ab1f61bba7d7caa9d6f","impliedFormat":1},{"version":"8cd19276b6590b3ebbeeb030ac271871b9ed0afc3074ac88a94ed2449174b776","affectsGlobalScope":true,"impliedFormat":1},{"version":"696eb8d28f5949b87d894b26dc97318ef944c794a9a4e4f62360cd1d1958014b","impliedFormat":1},{"version":"3f8fa3061bd7402970b399300880d55257953ee6d3cd408722cb9ac20126460c","impliedFormat":1},{"version":"35ec8b6760fd7138bbf5809b84551e31028fb2ba7b6dc91d95d098bf212ca8b4","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"68bd56c92c2bd7d2339457eb84d63e7de3bd56a69b25f3576e1568d21a162398","affectsGlobalScope":true,"impliedFormat":1},{"version":"3e93b123f7c2944969d291b35fed2af79a6e9e27fdd5faa99748a51c07c02d28","impliedFormat":1},{"version":"9d19808c8c291a9010a6c788e8532a2da70f811adb431c97520803e0ec649991","impliedFormat":1},{"version":"87aad3dd9752067dc875cfaa466fc44246451c0c560b820796bdd528e29bef40","impliedFormat":1},{"version":"4aacb0dd020eeaef65426153686cc639a78ec2885dc72ad220be1d25f1a439df","impliedFormat":1},{"version":"f0bd7e6d931657b59605c44112eaf8b980ba7f957a5051ed21cb93d978cf2f45","impliedFormat":1},{"version":"8db0ae9cb14d9955b14c214f34dae1b9ef2baee2fe4ce794a4cd3ac2531e3255","affectsGlobalScope":true,"impliedFormat":1},{"version":"15fc6f7512c86810273af28f224251a5a879e4261b4d4c7e532abfbfc3983134","impliedFormat":1},{"version":"58adba1a8ab2d10b54dc1dced4e41f4e7c9772cbbac40939c0dc8ce2cdb1d442","impliedFormat":1},{"version":"641942a78f9063caa5d6b777c99304b7d1dc7328076038c6d94d8a0b81fc95c1","impliedFormat":1},{"version":"714435130b9015fae551788df2a88038471a5a11eb471f27c4ede86552842bc9","impliedFormat":1},{"version":"855cd5f7eb396f5f1ab1bc0f8580339bff77b68a770f84c6b254e319bbfd1ac7","impliedFormat":1},{"version":"5650cf3dace09e7c25d384e3e6b818b938f68f4e8de96f52d9c5a1b3db068e86","impliedFormat":1},{"version":"1354ca5c38bd3fd3836a68e0f7c9f91f172582ba30ab15bb8c075891b91502b7","affectsGlobalScope":true,"impliedFormat":1},{"version":"7e20d899c28ca26a2a7afc98beaa69e63ff7fba0a8bc47b4e3bf3ede5e09e424","impliedFormat":1},{"version":"2d2fcaab481b31a5882065c7951255703ddbe1c0e507af56ea42d79ac3911201","impliedFormat":1},{"version":"a192fe8ec33f75edbc8d8f3ed79f768dfae11ff5735e7fe52bfa69956e46d78d","impliedFormat":1},{"version":"ca867399f7db82df981d6915bcbb2d81131d7d1ef683bc782b59f71dda59bc85","affectsGlobalScope":true,"impliedFormat":1},{"version":"372413016d17d804e1d139418aca0c68e47a83fb6669490857f4b318de8cccb3","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e043a1bc8fbf2a255bccf9bf27e0f1caf916c3b0518ea34aa72357c0afd42ec","impliedFormat":1},{"version":"b4f70ec656a11d570e1a9edce07d118cd58d9760239e2ece99306ee9dfe61d02","impliedFormat":1},{"version":"3bc2f1e2c95c04048212c569ed38e338873f6a8593930cf5a7ef24ffb38fc3b6","impliedFormat":1},{"version":"6e70e9570e98aae2b825b533aa6292b6abd542e8d9f6e9475e88e1d7ba17c866","impliedFormat":1},{"version":"f9d9d753d430ed050dc1bf2667a1bab711ccbb1c1507183d794cc195a5b085cc","impliedFormat":1},{"version":"9eece5e586312581ccd106d4853e861aaaa1a39f8e3ea672b8c3847eedd12f6e","impliedFormat":1},{"version":"085f552d005479e2e6a7311cdbbe5d8c55c497b4d19274285df161ee9684cd9c","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"007faacc9268357caa21d24169f3f3f2497af3e9241308df2d89f6e6d9bb3f2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"74cf591a0f63db318651e0e04cb55f8791385f86e987a67fd4d2eaab8191f730","impliedFormat":1},{"version":"5eab9b3dc9b34f185417342436ec3f106898da5f4801992d8ff38ab3aff346b5","impliedFormat":1},{"version":"12ed4559eba17cd977aa0db658d25c4047067444b51acfdcbf38470630642b23","affectsGlobalScope":true,"impliedFormat":1},{"version":"f3ffabc95802521e1e4bcba4c88d8615176dc6e09111d920c7a213bdda6e1d65","impliedFormat":1},{"version":"809821b8a065e3234a55b3a9d7846231ed18d66dd749f2494c66288d890daf7f","impliedFormat":1},{"version":"ae56f65caf3be91108707bd8dfbccc2a57a91feb5daabf7165a06a945545ed26","impliedFormat":1},{"version":"a136d5de521da20f31631a0a96bf712370779d1c05b7015d7019a9b2a0446ca9","impliedFormat":1},{"version":"c3b41e74b9a84b88b1dca61ec39eee25c0dbc8e7d519ba11bb070918cfacf656","affectsGlobalScope":true,"impliedFormat":1},{"version":"4737a9dc24d0e68b734e6cfbcea0c15a2cfafeb493485e27905f7856988c6b29","affectsGlobalScope":true,"impliedFormat":1},{"version":"36d8d3e7506b631c9582c251a2c0b8a28855af3f76719b12b534c6edf952748d","impliedFormat":1},{"version":"1ca69210cc42729e7ca97d3a9ad48f2e9cb0042bada4075b588ae5387debd318","impliedFormat":1},{"version":"f5ebe66baaf7c552cfa59d75f2bfba679f329204847db3cec385acda245e574e","impliedFormat":1},{"version":"ed59add13139f84da271cafd32e2171876b0a0af2f798d0c663e8eeb867732cf","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7c5e2ea4a9749097c347454805e933844ed207b6eefec6b7cfd418b5f5f7b28","impliedFormat":1},{"version":"b1810689b76fd473bd12cc9ee219f8e62f54a7d08019a235d07424afbf074d25","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"00cf4001e0d9c6e5e036bc545b9d73e2b8b84cddb02e61ad05bab3752b1d4522","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"556ccd493ec36c7d7cb130d51be66e147b91cc1415be383d71da0f1e49f742a9","impliedFormat":1},{"version":"b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","impliedFormat":1},{"version":"95aba78013d782537cc5e23868e736bec5d377b918990e28ed56110e3ae8b958","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},{"version":"26e0ffceb2198feb1ef460d5d14111c69ad07d44c5a67fd4bfeb74c969aa9afb","impliedFormat":99},{"version":"efa235fa23d71829de136935b91c4048f4741f33c3f5bcf5a90b01c29c2bd07c","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"eb5b19b86227ace1d29ea4cf81387279d04bb34051e944bc53df69f58914b788","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"87d9d29dbc745f182683f63187bf3d53fd8673e5fca38ad5eaab69798ed29fbc","impliedFormat":1},{"version":"035312d4945d13efa134ae482f6dc56a1a9346f7ac3be7ccbad5741058ce87f3","affectsGlobalScope":true,"impliedFormat":1},{"version":"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f","impliedFormat":1},{"version":"889b2c3eb9e1589df4ed4d28ef37e25be191e1dc4f7dd82d600ce79811800ff3","impliedFormat":99},{"version":"ae046314c0651da4a01e9e48ddf370ce9d22ad21f48962f25a12c1c09de9b01a","impliedFormat":99},{"version":"d0e136d6bf3c38be7af296b7e01912b6e8944a428ba7fd1e415a10acd9e687e8","impliedFormat":99},{"version":"7a685305685db7f9d2195ae629df44ae5888c13371a032ebe629a615a177a45b","impliedFormat":99},{"version":"383653e7a2888455e907c0adbde5bf74f16d95f40b73c81ffe0cc982b21745d5","impliedFormat":99},{"version":"c95ddf25be5f33178641ca540ca1ec157f8bdceb1f3c430d0c7045af5af04404","impliedFormat":99},{"version":"c3aa1b9d09adac7ac5e49aba8e8fa7114c2c842d46c2c5f51da53ec889787bac","impliedFormat":99},{"version":"7cd8fbd00f9608795145d427ff641d7abc485cd485d833ea1d9a90222ee73778","impliedFormat":99},{"version":"0f4f54801406a0a67455a9ad950bed9f4d2921fd66a91682f83a985086d60082","impliedFormat":99},{"version":"23e2aa3fe97254db69a9ba8f6b62e9397a5c64fd080e2dbf70d9004f226a224a","impliedFormat":99},{"version":"77d9b19c200d3803c1b31a29dd65cb977983e9b81dd797fab892e70bbe6b9679","impliedFormat":99},{"version":"a189ede87046071ae9cd84e90d09327b3ab44b5e73947d38eaa56c2e839b0fc7","impliedFormat":99},{"version":"ed2d9201cc939621497a3d92ad39828a8823b7c7fe64f6c41d43792204e60f97","impliedFormat":99},{"version":"3f05e2ba96cd23e6a53cfea66bc100621c4703bf6d46515b841dd6892286b08e","impliedFormat":99},{"version":"6e5aa91099e2fe5d1d05f6f3100a90e5a5d9b8aea7b0ea6f4d05a0f192899a64","impliedFormat":99},{"version":"bd85cba544b37cd32e8d02b138c3a2a4075930d01146b3f5e33d713b39dafe77","impliedFormat":99},{"version":"04a7116aece3802e7ee128fed47d31cd18e5660825a62b42a62929f9508b936e","impliedFormat":99},{"version":"20ca05d62223bf6f117925ef8f9b9781e894cb146d30ac491e0763d34e53a5d0","impliedFormat":99},{"version":"4ba733d1a5ff0a0779b714468b13c9089f0d877e6fbd0147fac7c3af54c89fe0","impliedFormat":99},{"version":"697203f3f5a1fea90e40fe660360325090ab36e630dc9422a1909dd4faa2cacc","impliedFormat":99},{"version":"ad1226eba93a65cdccdb1b4f115d67c5469e12705dbe80139c2988d6b296d04d","impliedFormat":99},{"version":"4ea2c94c3a1c87029d10f11c209674d4c6a0c675a97503dc9668d2815ff6ea11","impliedFormat":99},{"version":"63ec0a7e0f0b1ac673102c02c00969d992d7dde30d7066d941f0c3e2c9e80610","impliedFormat":99},{"version":"31ace06421fa71c192f4b9de6ccf36dbe0ee3534a237ebafce0a2215bb9455f1","impliedFormat":99},{"version":"94cfe3be66e4a6a1d52eaff0eb03bea21b4cded83428272c28feedfa5f9a152a","impliedFormat":99},{"version":"c2cf5eb33fc641dd321afd12c726ac3e753a81ab1618270ce6cd508f927989c7","impliedFormat":99},{"version":"a7f2f38cd72a96e7678555a1166a4488771b94e5a9c799d1c8943974ada483bd","impliedFormat":99},{"version":"c519327110a82e5eeaad683dc64f36994f19d9893fe69c4ea2b19d41b7e3e45b","impliedFormat":99},{"version":"23af35a045f9117250e060abdb2789bd34519eb5a6308463f299975a205b2d8c","impliedFormat":99},{"version":"9eaaedc489e28c9f7ff513bc094fe82da02cf2c4a3b2b35fe025699fcc08be78","impliedFormat":99},{"version":"73c4f628937d4e4a94d5af1c04bf57008a9d2c5f94a8fe6d9da8d51783069e15","impliedFormat":99},{"version":"1a7bb0d5979c3081b835f51a7a54b50c50500a897792b66b26a4b8583162ce4f","impliedFormat":99},{"version":"4cd02f2d4d7feae05b035dc1c451070f7536601f4f060d0e944959f1036b3b18","impliedFormat":99},{"version":"75ad38105b8decc3c60ee068c8d76e3f546b4db1ca55255d0a509f45e4b52990","impliedFormat":99},{"version":"6e16ba58508a87f231264a5e01b0859669229a40d6edea4485ac2032ddf8a7c6","impliedFormat":99},{"version":"8a3576bf6a6c46ceaf02e1a8fb56146b9271c5194ff8bf171156d0467946565f","impliedFormat":99},{"version":"d45218d368df27abcfd0253d4b1287e1b954156f32ff263f31913bad81a80918","impliedFormat":99},{"version":"0df2fda4b69934223c08e3b8dc5ddb620e2c0636e973960e624f309a330229c0","impliedFormat":99},{"version":"8e67c31beb3a87366e5eaa2f0399078be858b5b05b6b8209e594be97b69b020e","impliedFormat":99},{"version":"49455da231ef295ce5fdc0baa019df7622f4b9dc136677109cda3bd20824e903","impliedFormat":99},{"version":"7e92bffc685fdc3d8e04df0218f616022fc571ee8c057e95922e653759576816","impliedFormat":99},{"version":"9f404e614b2c4cff62c24a47df9e91c72bf3ff160624deac897bb35552b43842","impliedFormat":99},{"version":"715a0154ce1359c3387d467981bc331da06822a9b999bbdf5c483696e28a4ceb","impliedFormat":99},{"version":"38c9d54cbcd3022f7b8bac65e76b05fc15c318f53a114a5bcc2d69c33a182adb","impliedFormat":99},{"version":"86c183d6201f35c841e31d97ede84989e4c02b690786f7c11efe025ec2f15c05","impliedFormat":99},{"version":"f0258631e56de39d236a0a34aed46b28859d376949ba040204513e61fb2dfb97","impliedFormat":99},{"version":"6b5db77219e73ae487db6ad94fb2048b0bb051b4fdce283600a763cf63b14e8e","impliedFormat":99},{"version":"de5d4a4fe9ab759003078db5588ff1d6b4727e164b6ad570859cfdf06a9bd47e","impliedFormat":99},{"version":"86ea856c59eb2c4d2090f22510d63631393af4793c98f0ad100f4545f0c9088e","impliedFormat":99},{"version":"5ef5bf461006cf3fe5bf50c5aed8d4fd5ba4aed06105428d4df3733136c9d9d7","impliedFormat":99},{"version":"6a074d0008571a6d1396fb1d86e54bf22bfb38fcd8a4df1e61cf4ca309817fac","impliedFormat":99},{"version":"bfedd3882083dc1f013092f079f76c17b6ea95594fba33a3e56c222ca984f263","impliedFormat":99},{"version":"7d89a609d9e50597111dde64263b0708343a68db2d53aee0bc2d3bcab7e745c3","impliedFormat":99},{"version":"0b840392f948eeb18059b0478c2073ac222a467829df29ef0eb1f15f55a3c77a","impliedFormat":99},{"version":"c91b058ab74323c57dda1cbda7eb8cee56272002249a642deebbbd977c4a0baa","impliedFormat":99},{"version":"cb7f489960477f1f432a3389f691dc243ca075e87f20032a2866321dab05bae2","impliedFormat":99},{"version":"ca885b971dc0c8217ef8aca9f3879c3c2d53415c4dfbe457748045160f6e5205","impliedFormat":99},{"version":"b8019033d2c520e44e54fb15b5865eca6b2939920ea8002b37e263d3187d5b71","impliedFormat":99},{"version":"a4e67e8eb5df4e6630588b1ce5c114d1fa5a533eb1f45c2ee4ecb4f48dab7788","impliedFormat":99},{"version":"3f8e1b42b7e6aa4756e00b26c972a64eac1b747600b2540120f95a92cb1684e4","impliedFormat":99},{"version":"93d5f2949aafca2e199761d2ded0b4870446685897b4d2214d0a9d9d581e0538","impliedFormat":99},{"version":"7c7a960997d3470573faaaa089e6effd21cd6233d97ba7245974b4adf46597fd","impliedFormat":99},{"version":"2bb814f26a57746ff80ff0dee91e834d00a5f40d60ee908c9c69265944c3a8b5","impliedFormat":99},{"version":"86e035d87d8f9827b055483b7dfdb86ecbb7d2ca74e9dce8adeaf6972756ac03","impliedFormat":99},{"version":"73ac47e45d90fb23336a6a01512ae421275cb1c818b7a5068ec84f58e94a5999","impliedFormat":99},{"version":"b5c081fa1ad246f75cd10b822eebdf35c3e1c510e9ad3a89570fbe4c575e0cb1","impliedFormat":99},{"version":"017907864b01ae728f5be6be99ea7632e68b2a35c2d7c9606bde20f85f10f838","impliedFormat":99},{"version":"a86a5d2ab15be86342869797f056d4861fd0b7cfae4cfa270121f18fe8521eab","impliedFormat":99},{"version":"22f98eae982b7f0d26d3dd7849210e033dc1992f594d87c6fe30075eb94b7a24","impliedFormat":99},{"version":"ec47b34311c3c799d1c90a3dcac1651ed23948c064aca4f0617fa253e648ab15","impliedFormat":99},{"version":"761efac4dfd849586e4fe49fc6cda2aba8e708fa8e4eb19ae85373084cba0d51","impliedFormat":99},{"version":"899ed4016a7a722a6224e78139286f1ab7d05f79be50af0a6492b95170e56fab","impliedFormat":99},{"version":"965bfde0433a808a389b80a8e45b717cd2d5a3a0cdf418707cfda3046e33fa5e","impliedFormat":99},{"version":"f5f69e449b4dc8e31cd0315860c59fde6358408f19747b76d98c90773ca62b19","impliedFormat":99},{"version":"6f623e7a3b9de6f69a0b2f81413d4dc357017ebbda86f12153346a52f1e2a739","impliedFormat":99},{"version":"e44950769f3c3b4ee2fa3b0a19c1cfd190c4e8378287ce8a2dabf821c7b9c2e3","impliedFormat":99},{"version":"9a9a3212ac108de497464fc14ab2178cfa037eb981a5b0f461e13362fdd3851a","impliedFormat":99},{"version":"b011f71b5d21579da9f868e56acf3887051fc4027cc7cde7317facb232ed3e95","impliedFormat":99},{"version":"7714308befeeb34cbc1d6715bb650d05e2b4e0516db9e58ef4c399e462d222b1","impliedFormat":99},{"version":"81e2b5cdeccd368e2eaaf33003fea50bd39740f8a8a71a1a487458435b7695dc","impliedFormat":99},{"version":"919b86dd53cd38daf8207cac48b67824ad2b8c9daffe559e1b0ec75f39d21ba8","impliedFormat":99},{"version":"aa7a83f4acf2686925511ecc32d148062c02984068d563c44f00835fee5b164f","impliedFormat":99},{"version":"d4632bbd2d2afbb1b75163dc7cabab5cc218c2fa933cb8f7d5b7089255faa6fd","impliedFormat":99},{"version":"0cf4827f19c749c5befed9585862c6196a4a5b3d889d20e0f5f4bdb6f734dcc7","impliedFormat":99},{"version":"14d3c7499d1759af5c78eec4f26a6f5b85bdd5b0e41ef3f5e6e813f1ae88c06a","impliedFormat":99},{"version":"0082935dc2cb31cd632eaa6bbdec17f1a9142652e38ede025c0ffab00c50bac4","impliedFormat":99},{"version":"0df7497ada3a4f6459420803ecf7e555f1ad1e7bd43c1e17bdafbc34e19d7162","impliedFormat":99},{"version":"5cccc8d1dd17c789bb6baba06a035e98e378a80d133da3071045c9901bee0094","impliedFormat":99},{"version":"c8a40bb3df60346af02e8d786473985ba53b716bc7caefd21ab838f025ec103b","affectsGlobalScope":true,"impliedFormat":1},{"version":"64da9a17f7cb5d84731607aed8493e4550a3e613cc7b880c87ce82b209d66b96","impliedFormat":99},{"version":"1e4bccd328de23aafb5a566317b3fa580aa2caa1a9146cc0b9effa792ba48ea9","impliedFormat":99},{"version":"156ac329be3116b9c1f55ae3cdf8e7586717561ac438ee6b193e7c15b2c87b1a","impliedFormat":99},{"version":"6d056661e4b636cc04e36c36b24a4eb692499b21fe0b18cb81f8bb655d7a3930","impliedFormat":99},{"version":"3481e087d798439d58148bb56c2320d555416010a93d5088888f33c1094fce0c","impliedFormat":99},{"version":"1601196196feb9bc15f41c1abe032669bb1687304cdc644f67e9ec8d99e1252d","impliedFormat":99},{"version":"1f957c8657bb868e8cb92e46eac8c8b1877a96708e962015a1ed47fd42c697f6","impliedFormat":99},{"version":"217800577a2c9a7232e5a9d1abd1c1836acbb004e7522a5261299aa867713f96","impliedFormat":99},{"version":"60981ae7c2a8926f7855d8068c42e05a3b1959f0bb795a8bb9773c912a9a6f16","impliedFormat":99},{"version":"4a6de5821d23f5e1781c567ab6550e5357b2c2ae3e8813a277062512f73d4a28","impliedFormat":99},{"version":"618b5aa1f8b9791938f8033f1855238774b555f9dd35f0b8a5443cc066721605","impliedFormat":99},{"version":"58bdc0c8212671d213ad6456b1851e4b4473fc0c63891e362a7513187fea1736","impliedFormat":99},{"version":"b4be747af2c3ceddb397190b4346be5612761526d4a8f5f9015c04e7aa73f1d6","impliedFormat":99},{"version":"2022dfa3df40a1f11d5f354bd729d0a2bb69d0d12b62767d3b237a29e6c62d4a","impliedFormat":99},{"version":"68617a52d0596e488c88549c000e964c5f6a241e5361095b2c6203586689b1f3","impliedFormat":99},{"version":"8d4a70e05b1f8450f5fb8997e5bfc336dd0baec3f2c8117f6f260d4eb68de0ac","impliedFormat":99},{"version":"01e6524f28e8d3fad9e13c43a27eaca96e88ca299f0a4f7da074143c251926e9","impliedFormat":99},{"version":"e61ce3bbfe37669692af8ac289869baa7b9d01b7e260e5cd0294095a4f6c29a2","impliedFormat":99},{"version":"0e6b3c7f300f6e2587c62783ebf78c74e61e7e85d37591e1e1ecf82cc15adc01","impliedFormat":99},{"version":"f78f6212fdebbc513a6656389ecf3c4bd9e77f79c0d2da2250de961b386a67a5","impliedFormat":99},{"version":"5de56154de88f7bbad618a1aac7dcfbf8234785cb8821b00c6902208587409f9","impliedFormat":99},{"version":"a4f4ecd42fc62ae32f9fa03100f821c61a2ca3d5fe2a9c0720baddbd67ad3174","impliedFormat":99},{"version":"b41dc4272747d7b9e3f5620815fd1aece9bc2c0c09e00c4101b429216599412e","impliedFormat":99},{"version":"4c136da3b1dce49c12eac152699c6b4bc64fa93d6c7224a43c816f7e51b00930","impliedFormat":99},{"version":"1093df5dbb38c416c10e41b3379033e952cb26cfa2a667bdf182f55dcca0d7e9","impliedFormat":99},{"version":"4d42746407b6732df92275e20f311f9717b57f1e3a90cf71730620077a7daf5d","impliedFormat":99},{"version":"72635b405f1d979eee2110b7d2921470748e13b19adbf42887c2680964af6f30","impliedFormat":99},{"version":"3a719c9c30a20a413b97a458f411679bbe56a4de8ddb2f3ae7cf2639e86d0e0f","impliedFormat":99},{"version":"ea37a7bc8718a01eeff979fef574318d7a5915fc786c74582c86cb553bee484b","impliedFormat":99},{"version":"6c61ff540eda59f07484aa863b753d7d6a8de0ac907e0e912ce2835f2e86e167","impliedFormat":99},{"version":"b90f14bca14cdbdd60dc83c451aca97e8df63c8eb8a158a9ed84de4bfb4cad76","impliedFormat":99},{"version":"bcdfa0b735dff249e6cafe7096d17d338c3942704c20b0cacf78c1d78a5a427f","impliedFormat":99},{"version":"daf3cb7fbb067540163df0a3421e791ebde6bd2e699aad4cdb13366871cb7196","impliedFormat":99},{"version":"98ba4768c426848773fb4a39203aac92e6baa545d93510665cdf207454d0811c","impliedFormat":99},{"version":"f65116ea54fd65813a0d9695249ceaa716487c932247e4aede3e2e3ad3d07316","impliedFormat":99},{"version":"99484c7a277c488a16c49ac1affe465e4fbb5e4d57b8c2190092c5d7b4fe6fca","impliedFormat":99},{"version":"c29f7f5c851ec3a781a17d7afb9280da6adfc9748535481b381daf5a67f439d0","impliedFormat":99},{"version":"0f1ea4f6570d745ee2dfa784baa306ae15c35ff7742566ac5ccc1a893af9a1ba","impliedFormat":99},{"version":"06e727ca4d41b4f549f875d7999d940a392058b1b579846441351ff011a63a31","impliedFormat":99},{"version":"6c7f1a4f3d43a47624bdf26e93be7be9fe29cda02de5b53b83f5c7559ae07745","impliedFormat":99},{"version":"d94acd15b4a3517523756dfeabcb7b4fb8ee853bba680d892ccfd3df4c81edc1","impliedFormat":99},{"version":"0f65f9b61383ffcfa1a409da90c35741cd81ece1a2dc6f2ebd094d81599bc5f6","impliedFormat":99},{"version":"9abd03a84d5473e66b038270dbeae266129ab97261d348a5fbd32ec876161a85","impliedFormat":99},{"version":"07af0693d07d8995441f333cc1fd578c8dc28500e1e43bbc3e1656b24bc19d03","impliedFormat":99},{"version":"b6024c6222886b95cb29ab236155a98f8e5dc41151233781815e81a83debf67b","impliedFormat":99},{"version":"94dab3752006a2cd2726462342f1775ef18ff4986404d016d317fe79a9d0a14c","impliedFormat":99},{"version":"727b3a462015bbed74b520861445761ebaecf94e09d95bbf59dfcf22afaccae9","impliedFormat":99},{"version":"2c0300921d8d04b21353c94a8f50a2b6c902feccd1303b6f136bedbb2cec5ed1","impliedFormat":99},{"version":"d496217c7f38f218fc162e8f3e6ed611343aa65615f730f82c494dee6c892bc0","impliedFormat":99},{"version":"282ed4ab5b5c4759d5c917c51a5b2f03ca1df4072275b6bccb936cf60078e973","impliedFormat":99},{"version":"2c96813e14e7edcd8e846f009b24fb1bd842b90e2dcd85481136e52588de7982","impliedFormat":99},{"version":"aa70da8072bb8b6e8fae35c7d394d543be8e5c946dad666225a3475010fd2bf0","impliedFormat":99},{"version":"d2c35cb9836cae1899ae9e7e114410dc128bcff4a79cc26318db285699e0223a","impliedFormat":99},{"version":"f89fbb50fd3736e09b418a2e66b98ff9a04820259856afe54bc67977e1acd05b","impliedFormat":99},{"version":"4c76aceec7002f299d9a57ec8e6623f3573bea208b1ea51cc5ea03bf140adad4","impliedFormat":99},{"version":"a0f217b01453d43058cea514325ac8bd3ac3a184265314429eec8059c62824b6","impliedFormat":99},{"version":"c73e552e79763809a52f967e48b7a96a0c164c672ef99de1fa8a7e9e02e3b177","impliedFormat":99},{"version":"3bb351642082a63b4565d8354455bb752daa8902451cd851d6235e04cfaff5a9","impliedFormat":99},{"version":"caa4ee2fefd75dd8bf98a9421e3f99f6c7e70c30b21e78384ed23903a04579e5","impliedFormat":99},{"version":"aecd83ca7059d21a33fb7ed01dfa06a36c545698dbe0017073dba45532a8487d","impliedFormat":99},{"version":"7fb874c17f3c769961d1b07b6bb0ef07b3ca3d49da344726d8b69608997ef190","impliedFormat":99},{"version":"979e969f86456425e505f6054f5d299f848223d70770a5283fa7c405020b47e1","impliedFormat":99},{"version":"7235f928c14f752f6ea3d6d034693c5d46154cce8757a053d9cd6856be2ab344","impliedFormat":99},{"version":"acd7f9268858029bcec5eba752515b9351d4435b21f1956461242c706dcc0cf9","impliedFormat":99},{"version":"53e2856f8644978742fae88b3c7f570ab509dc4d13288b3912a4446993fa3bc7","impliedFormat":99},{"version":"ea2b6112bfd326f1075896bf76c9108dfd08ccbae2482ba31f68ca43f0b59ca5","impliedFormat":99},{"version":"3f9368aa15d0cc227a3af7af3e3df431dadf0f7cd9897fcc54507f7eb68761cc","impliedFormat":99},{"version":"0f2d4be859066fc3ea8d04b583cd0774e1f9dce7f60b9890bcc0a10efb9fac33","impliedFormat":99},{"version":"ac09b9131c553c189311d9e94d3853b7942d0097925304fe043220a893701ce9","impliedFormat":99},{"version":"f1b34ea3d64f73fc79ce1f312589134db27aa78ef9e156a8f14f89f768e800ac","impliedFormat":99},{"version":"873da6c837a1ee62b5f9b286845be06dc887290a75c553bed7f431107d25a3b6","impliedFormat":99},{"version":"b2abee3c001c024d4e552c4a3319bf3fcc94a1f48bb0d21f5d300d9b4920bde9","impliedFormat":99},{"version":"f9740d044306830442cac761b593538117f46c5ea57a8dc6d61f0bee12e971b6","impliedFormat":99},{"version":"7cf786964e26f0e2c3a904f93f6e31609e2636723df8c1ce248d39b55055c89f","impliedFormat":99},{"version":"41c6aff52e4289763ea30f0849b712437aaeb420c8448aeb8047ee2eca4549f4","impliedFormat":99},{"version":"f5db101f7d90f614627bcab5f8d06d9ccd144a1735b475637940c54097786b67","impliedFormat":99},{"version":"8c575a8e1b6032e576577f28d74066f73aefa7a35d741d0015be36956bbc30aa","impliedFormat":99},{"version":"1989cb4fb2174c56b15f8b10d18ecb0c053e7b39f94582581d69767d7bfb9b32","impliedFormat":99},{"version":"cdac1d3e70d332d213295dc438bf78242c29b14534adf3ef404c3e255c66e642","impliedFormat":99},{"version":"47921880701610e8d8a5930d0c9ea03ee9c13773e6665f4ffc8378d5f8c8c168","impliedFormat":99},{"version":"41cbf6c58f2f4e1e5ee95a829b3f193f83952385fa303062f648040a314f939b","impliedFormat":99},{"version":"bb11cd0d046d21d4ae4a28fc4b0eb5d9336a728f9bd489807a6a313142903bc1","impliedFormat":99},{"version":"a96d6463ab2a5a4cf31b01946f1b0929dc3f8be9f28c7c43da29a9e6b7649db1","impliedFormat":99},{"version":"ec43d6b21fd1ed5a1afeb779ceba99e80fe010458bb0a67d9ef301426b1929e5","impliedFormat":99},{"version":"105bb5317c5212d56f82fd9730322b87f4ad8aea2927ef7684341afad050f49b","impliedFormat":99},{"version":"79ffce57ab318282b29bceb505812c490957124a3a96c7d280a342488b0859bf","impliedFormat":99},{"version":"06fd0e1204b7daf4164533fff32ab4e2c1723763c98794f51df417c21e0767f3","impliedFormat":99},{"version":"c4b46086b44bb8816d4a995654c00f64b3601eb50a163f2bba4dfe48ae6c6b91","impliedFormat":99},{"version":"32e670209322bd3692e8fc884c63002f6bd565e83f62f1fd23c46729aa335d1b","impliedFormat":99},{"version":"97717d35deb9f6a6127f3abff60c9af080ab0ccba60aa06a5a3486a374747573","impliedFormat":99},{"version":"4d70c89489fdef067b0819f22eec5fd0323a8b488d93075cb7953bbfc636e03e","impliedFormat":99},{"version":"233dc7f3ea55d2375b32c5c19034babec8e1496dc73784f9b091629a5287f2fe","impliedFormat":99},{"version":"e3fbf3f3e99083f8fc21bbde7677c3b1cad0c730fe231599a69911aa66487d01","impliedFormat":99},{"version":"59110c7d72a09bacde4a80f4ba95d9990b352911f0e4ea09bf766804f8d3e44b","impliedFormat":99},{"version":"3d827d1dd689311e57a98e476b3451445d39e573f4855ac265b7ec1747075c4f","impliedFormat":99},{"version":"e0669b0e7c953962035bb39e7fdfd5cc8fc3d9a666a8b167b78417355609be01","impliedFormat":99},{"version":"8495eef8be427c71a2d574e3ead06c537a9a6d437dd669e6786dab3df009f125","impliedFormat":99},{"version":"15741df16deef60b197560d3cfe45e6c1eff69fa7b85a861e3d8aa8a26683b83","impliedFormat":99},{"version":"802fd034cf22379b22a681e021d7ecc9073c01fccff1eb737f54ee2c6fe4395c","impliedFormat":99},{"version":"bb77b52bead9b75d7173bec685e5e2136f6c3f226cedae736db63a44f69db679","impliedFormat":99},{"version":"b3f7783d4977af919bdb8db798fe185908083c6f4bd3b07460967c8e093f7312","impliedFormat":99},{"version":"5a6bae49831f960e7f0bc66f49b2c40077b136d9573871f865507fde09580436","impliedFormat":99},{"version":"c9d03e6b230acfabb058a0b0391312dfb0e7001bb5955836333a14c7f9347f3e","impliedFormat":99},{"version":"e6295124f95b686a16233c1031d04cd971f9685e3416631f463bde75a5c86ce7","impliedFormat":99},{"version":"00c38bd1fe89fed8d4e8502db4f896aef7415b097ac061c2d65f2b539b6df6a7","impliedFormat":99},{"version":"94a2d7c15538d8e83415299f17fd00ab88c594b6a0a40be1e26c99febbab45f6","impliedFormat":99},{"version":"20bbd68ac2d2e7cdf9f60816ba9b378e13c07f0fdafccf9ae5833c876c6f51bc","impliedFormat":99},{"version":"df109d2490b693bd75105efaae08738ab84102bfdb2eee2372e9e3f369ec5fc2","impliedFormat":99},{"version":"0fabc5da6eb8454effc526d74f28b0abbe726eab0ed1296aa618b611da7d9462","impliedFormat":99},{"version":"d411ba0bcd6a51485be855a01cb95f79649fa90039b4f235ba8481dc68edae3e","impliedFormat":99},{"version":"b1991f24f264ab5e0d4de1a95b8483830ba659016dfe4b9e58b4076974c1966a","impliedFormat":99},{"version":"b8ba23b2e323342f2710619f6c1abf6731da764092cdca12f09b983ebf236d8a","impliedFormat":99},{"version":"6e688e8aeba98c268b195f80355a8d163d87ac135ad03c708ceda608e6e269b2","impliedFormat":99},{"version":"802a6978c1b38822934ce43a3505e13b555584848c50bc5db9deb2e896c0940e","impliedFormat":99},{"version":"f502c7d829f5774109007ec2262c23efc941dd1ce42acc140f293a7c5ccfd25b","impliedFormat":99},{"version":"af3444bd00030bae3bef81569f8703ecddc2e569cb6b728ec045f0d73d47572b","impliedFormat":99},{"version":"53102281f8a153bb051e0223a8dc51ff9c4cf92da127d91e3f60e74b4e8f41ca","impliedFormat":99},{"version":"e402e111fadcd36fa26ea1ad74f3defd6ef478f6d278a69c547e664b57770392","impliedFormat":99},{"version":"bf8f4b3b372e92a4e4942ce7f872b2b1e1bd1d3f8698af21627db2dee0dda813","impliedFormat":99},{"version":"ab8b67c1f76b6586467a8bf7a2e855e8c2eb4e386f1d264dec1d8d877de7210c","impliedFormat":99},{"version":"d6325d809c8396ecc90202ebfd2427e052a77d98cfd4e308f656346baf84106b","impliedFormat":99},{"version":"dad5c38d723d08fc0134279b90fac87441ee99b71b0d30814b86954e0111d504","impliedFormat":99},{"version":"a29375cdd094d8ea5180422fb278f6bcffdeade0280d86d24d77b017a6144833","impliedFormat":99},{"version":"cef653b7f2115c8e2a9b6558bf9a083dbcc37ce8fb6bae0e48cde3b92fdaacb2","impliedFormat":99},{"version":"bb544ec93eab70a6c769cd69c0912742da7c2a8bed7d570e79b8af046a9ca556","impliedFormat":99},{"version":"532bd533a1921eedb9b39fa3559594ab783233867021a7a911db00be5d42fe7a","impliedFormat":99},{"version":"ad48586787d5e217f4fcc229e3c3d8de8aa12979fdf1f186134e3684d56577ac","impliedFormat":99},{"version":"229d6bca5145c86846793cb3166c83abb256cfdb5c425f25ada8eee49c993e54","impliedFormat":99},{"version":"292856f47dad178fe1cb3401554428b3b0157369a8fa52792587fd2bd06fcbec","impliedFormat":99},{"version":"46d372c701338579665c25fb121c836a402409cba9a03d14c403f92a4d6c7852","impliedFormat":99},{"version":"5c390ba8a6920d8d9072b0646388e5a4fa455d6e02cef29164f1c0b917a16f41","impliedFormat":99},{"version":"b8562e5aefa86c069ec1c61dff56ef0492e9fbd731cbcdd4d7fce28a8644e9f6","impliedFormat":99},{"version":"d4df0b60e8672c34a487c685ff7bee9d56ff755c61695bd63d152c331f768cc9","impliedFormat":99},{"version":"dd6c7d6abb025e7494d02fa9f118af4a5ab0217e03ae54dd836f1160cb7a9201","impliedFormat":99},{"version":"82d76af0a89cd5eb4338771a2a5b27f3cbc689b22be0b840de75be4cfc61f864","impliedFormat":99},{"version":"24e856aec3b5c4228ffed866dcd8e7e692aa86eccaecc4fa8205fadd9737d1af","impliedFormat":99},{"version":"fe395a24df9ffd344cb825575d4b35c1cf69275208c0f99517c715bd7d08ff79","impliedFormat":99},{"version":"39e8edcbd5ac35c6cfdf2b1a794a9693a461a54efb2a475ab7fc08ab13504e26","impliedFormat":99},{"version":"12012b6c28d09a6f1d86b2a30213a92a9e92ad9ee573f94c92a8b237b6422bb7","impliedFormat":99},{"version":"8ee28204ddb2be7d6dfb68891493f654cbf10f5e1667bd33bd62920d9eb9e164","impliedFormat":99},{"version":"b09669391dd3312b8a52242af7823a3c44b50c7dcdc216db8da88b679af46574","impliedFormat":99},{"version":"b71e7f69e72d51d44ad171e6e93aedc2c33c339dab5fa2656e7b1ee5ba19b2ad","impliedFormat":99},{"version":"440c9aba92c41b63d718656bd3758f8f98619dbe827448e47601faa51e7a42fa","impliedFormat":99},{"version":"d9cf429fa9667112f53e9bb67bb7b32eeb3697f524d01b9781b65247f1733da4","impliedFormat":99},{"version":"763ee96bd4c739b679a8301b479458ea4fd8166892b2292efe237f2f023f44ca","impliedFormat":99},{"version":"9b10d76e4436eb4ac33c0a5540a02ec881a2fbcfcccfbb9883ebadff7f1d35ad","impliedFormat":99},{"version":"701e25008d343bdd67e02c0ccdce4c2ab41d56645bff646b5dc25e4145e77a3a","impliedFormat":99},{"version":"7a891af63bf06f2be51ed3a67fa974a324d7b917f7b1d10f323ed508a6662354","impliedFormat":99},{"version":"efa0e3dff0199f00eaeb36925776e62419538f7263ec77a56d5612ac5abe9ee2","impliedFormat":99},{"version":"ae6114539394eed7b6305a6d788cb6d2fd94e256d7582f5111a1972ee5a1c455","impliedFormat":99},{"version":"26e8e64bbad8cec5562ca764330c89d2cbe72a10fd7d9f320f16cc25bfe4a1e5","impliedFormat":99},{"version":"3563a343e025cb849b94da85e8455dd89064dee213bc97bbed559f83d74c98de","impliedFormat":99},{"version":"e247d08f342c95e3fd58574f875af23cf50ef53edc811ab8e2accc2f25441f8d","impliedFormat":99},{"version":"420845f2661ac73433cbdc45f36d1f7ca7ea4eca60c3cbd077adf3355387cb63","impliedFormat":99}],"root":[189],"options":{"allowSyntheticDefaultImports":true,"composite":true,"module":99,"skipLibCheck":true,"strict":true},"referencedMap":[[184,1],[182,2],[187,3],[183,1],[185,4],[186,1],[150,2],[98,5],[99,5],[100,6],[55,7],[101,8],[102,9],[103,10],[50,2],[53,11],[51,2],[52,2],[104,12],[105,13],[106,14],[107,15],[108,16],[109,17],[110,17],[111,18],[112,19],[113,20],[114,21],[56,2],[54,2],[115,22],[116,23],[117,24],[149,25],[118,26],[119,27],[120,28],[121,29],[122,30],[123,31],[124,32],[125,33],[126,34],[127,35],[128,35],[129,36],[130,2],[131,37],[133,38],[132,39],[134,40],[135,41],[136,42],[137,43],[138,44],[139,45],[140,46],[141,47],[142,48],[143,49],[144,50],[145,51],[146,52],[57,2],[58,2],[59,2],[97,53],[147,54],[148,55],[192,2],[194,56],[190,2],[193,57],[437,58],[434,59],[436,60],[304,61],[218,62],[303,63],[302,2],[305,64],[217,65],[306,2],[307,2],[308,66],[309,67],[310,67],[311,67],[312,66],[313,67],[316,68],[317,69],[314,2],[315,70],[318,71],[285,72],[206,73],[320,74],[321,75],[284,76],[322,77],[195,2],[199,78],[230,79],[323,2],[228,2],[229,2],[324,80],[325,81],[326,82],[200,83],[201,84],[196,2],[301,85],[300,86],[260,87],[249,2],[250,88],[327,89],[340,2],[341,2],[342,90],[343,91],[344,92],[219,93],[220,94],[221,95],[222,96],[328,97],[330,98],[331,99],[332,100],[333,99],[339,101],[329,100],[334,100],[335,99],[336,100],[337,99],[338,100],[345,81],[346,81],[347,81],[349,102],[348,81],[351,103],[352,81],[353,104],[366,105],[354,103],[355,106],[356,103],[357,81],[350,81],[358,81],[359,107],[360,81],[361,103],[362,81],[363,81],[364,108],[365,81],[368,109],[370,110],[371,111],[372,112],[373,113],[376,114],[377,115],[379,116],[380,117],[383,118],[384,110],[386,119],[387,120],[388,121],[375,122],[374,123],[378,124],[263,125],[390,126],[262,127],[382,128],[381,129],[391,121],[393,130],[392,131],[396,132],[397,133],[398,134],[399,2],[400,135],[401,136],[402,137],[403,133],[404,133],[405,133],[395,138],[406,2],[394,139],[407,140],[408,141],[409,142],[236,143],[237,144],[296,145],[256,146],[238,147],[239,148],[240,149],[241,150],[242,151],[243,152],[244,150],[246,153],[245,150],[247,151],[248,143],[253,154],[252,155],[254,156],[255,143],[267,91],[227,157],[208,158],[207,159],[209,160],[203,161],[258,162],[213,2],[223,163],[411,164],[412,2],[198,165],[204,166],[225,167],[202,168],[299,169],[224,170],[210,160],[389,160],[226,171],[197,172],[211,173],[205,174],[214,175],[215,175],[216,175],[410,175],[413,176],[212,177],[231,177],[414,178],[416,75],[367,179],[415,180],[369,180],[286,181],[417,179],[298,182],[385,183],[257,184],[426,185],[427,186],[319,187],[261,188],[289,189],[418,2],[419,190],[251,191],[420,192],[290,193],[291,194],[421,195],[271,196],[292,197],[293,198],[422,199],[272,2],[423,200],[424,2],[279,201],[294,202],[281,2],[278,203],[295,204],[273,2],[280,205],[425,2],[282,206],[274,207],[276,208],[277,209],[275,210],[287,211],[428,212],[288,213],[264,214],[265,214],[266,215],[429,92],[430,216],[431,216],[232,217],[233,92],[269,218],[270,218],[297,218],[259,92],[432,92],[234,2],[235,219],[433,92],[268,2],[435,2],[283,2],[188,220],[191,2],[157,2],[174,221],[172,222],[173,223],[161,224],[162,222],[169,225],[160,226],[165,227],[175,2],[166,228],[171,229],[177,230],[176,231],[159,232],[167,233],[168,234],[163,235],[170,221],[164,236],[152,237],[151,238],[158,2],[1,2],[48,2],[49,2],[9,2],[13,2],[12,2],[3,2],[14,2],[15,2],[16,2],[17,2],[18,2],[19,2],[20,2],[21,2],[4,2],[22,2],[23,2],[5,2],[24,2],[28,2],[25,2],[26,2],[27,2],[29,2],[30,2],[31,2],[6,2],[32,2],[33,2],[34,2],[35,2],[7,2],[39,2],[36,2],[37,2],[38,2],[40,2],[8,2],[41,2],[46,2],[47,2],[42,2],[43,2],[44,2],[45,2],[2,2],[11,2],[10,2],[75,239],[85,240],[74,239],[95,241],[66,242],[65,243],[94,244],[88,245],[93,246],[68,247],[82,248],[67,249],[91,250],[63,251],[62,244],[92,252],[64,253],[69,254],[70,2],[73,254],[60,2],[96,255],[86,256],[77,257],[78,258],[80,259],[76,260],[79,261],[89,244],[71,262],[72,263],[81,264],[61,265],[84,256],[83,254],[87,2],[90,266],[181,267],[178,268],[156,269],[154,270],[153,2],[155,271],[179,2],[180,272],[189,273]],"latestChangedDtsFile":"./vite.config.d.ts","version":"5.9.3"} \ No newline at end of file diff --git a/packages/webapp/tsconfig.tsbuildinfo b/packages/webapp/tsconfig.tsbuildinfo deleted file mode 100644 index 4f51030..0000000 --- a/packages/webapp/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/App.tsx","./src/main.tsx","./src/types.ts","./src/vite-env.d.ts","./src/components/Address.tsx","./src/components/AdminsPanel.tsx","./src/components/AdminsTab.tsx","./src/components/ClaimLimitsPanel.tsx","./src/components/CopyInline.tsx","./src/components/EditableName.tsx","./src/components/FeeTab.tsx","./src/components/FundPoolPanel.tsx","./src/components/HistoryTab.tsx","./src/components/IntuitionAtomCard.tsx","./src/components/LaserFlow.tsx","./src/components/Layout.tsx","./src/components/Metric.tsx","./src/components/MetricsTab.tsx","./src/components/NewVersionBanner.tsx","./src/components/OverviewTab.tsx","./src/components/PoolHealthBadge.tsx","./src/components/ReclaimFromPoolPanel.tsx","./src/components/SafeAdminHealthBanner.tsx","./src/components/SafeBadge.tsx","./src/components/SetFeesPanel.tsx","./src/components/Spinner.tsx","./src/components/SponsoringTab.tsx","./src/components/Stat.tsx","./src/components/Tabs.tsx","./src/components/UpgradeAuthorityPanel.tsx","./src/components/VersionsPanel.tsx","./src/components/ViewerBanner.tsx","./src/components/WithdrawPanel.tsx","./src/hooks/useFactory.ts","./src/hooks/useIntuitionAtom.ts","./src/hooks/usePostTxRefreshing.ts","./src/hooks/useProxy.ts","./src/hooks/useProxyAdminRotation.ts","./src/hooks/useProxyRoles.ts","./src/hooks/useSafeStatus.ts","./src/hooks/useSponsoredProxy.ts","./src/hooks/useTabAutoSnap.ts","./src/hooks/useVersionedProxy.ts","./src/lib/addresses.ts","./src/lib/explorers.ts","./src/lib/format.ts","./src/lib/poolHealth.ts","./src/lib/safeDetection.ts","./src/lib/wagmi.ts","./src/pages/Deploy.tsx","./src/pages/Docs.tsx","./src/pages/Explore.tsx","./src/pages/Home.tsx","./src/pages/MyProxies.tsx","./src/pages/ProxyDetail.tsx"],"version":"5.9.3"} \ No newline at end of file From 9c397aceef560f1c10a59ce8ea1974c5d5467adb Mon Sep 17 00:00:00 2001 From: Wieedze Date: Tue, 28 Apr 2026 16:34:04 +0200 Subject: [PATCH 15/15] docs(webapp): document feeQuote helpers in SDK integration page The L-02 audit fix (commit 9f9c018) shipped fetchLiveFees + feeCapsExact + feeCapsWithBuffer in the SDK, but integrators had no discoverable example showing how to use them. Without docs, the path of least resistance was passing MAX_FEE_PERCENTAGE / MAX_FIXED_FEE, which fully opts out of the front-run protection that V2's deposit() guards exist for in the first place. New "Call deposit() with front-run protection" section in /docs/integration: - Three-step recipe (snapshot live fees, choose strict vs buffer, splice into deposit args) - Both feeCapsExact and feeCapsWithBuffer shown side-by-side, comment explaining the tradeoff - Computed `value:` line shows the right msg.value for the strict case (amount + pct + fixed) so integrators don't have to figure it out themselves - Callout explicitly warning against hard-coding the bytecode maxima ("opting out of front-run protection") Inserted between "Fund a sponsor pool" and "Canonical versions" so it sits naturally in the writer-side recipes block. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/webapp/src/pages/Docs.tsx | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/webapp/src/pages/Docs.tsx b/packages/webapp/src/pages/Docs.tsx index d53a8e3..22b2085 100644 --- a/packages/webapp/src/pages/Docs.tsx +++ b/packages/webapp/src/pages/Docs.tsx @@ -1396,6 +1396,56 @@ await walletClient.writeContract({ value: parseEther('25'), })`} +

    Call deposit() with front-run protection

    +

    + V2's deposit(termId, receiver, amount, maxFeeBps, + maxFixedFee) takes two caller-supplied caps. If the + proxy admin bumps fees between the moment your user signs and + the moment the tx mines, the call reverts with{' '} + FeeExceedsCap instead of charging the inflated + fee. The SDK ships ergonomic helpers so you never have to + hard-code the bytecode MAX_FEE_PERCENTAGE /{' '} + MAX_FIXED_FEE (which would defeat the protection). +

    + {`import { parseEther } from 'viem' +import { + IntuitionFeeProxyV2ABI, + fetchLiveFees, + feeCapsExact, + feeCapsWithBuffer, +} from '@intuition-fee-proxy/sdk' + +// 1. Snapshot live fees just before signing +const live = await fetchLiveFees(client, yourProxyAddress) + +// 2. Pick a strategy: +// - exact = strict, any admin bump in the same block reverts +const caps = feeCapsExact(live) +// - buffer = tolerate small re-pricing (here +10% bumpsallowed) +// const caps = feeCapsWithBuffer(live, 1000n) + +// 3. Splice the caps into your deposit args +await walletClient.writeContract({ + abi: IntuitionFeeProxyV2ABI, + address: yourProxyAddress, + functionName: 'deposit', + args: [ + termId, + receiver, + amount, + caps.maxFeeBps, + caps.maxFixedFee, + ], + value: amount + (amount * caps.maxFeeBps) / 10000n + caps.maxFixedFee, +})`} + + Hard-coding the bytecode maxima is opting out of front-run + protection. The deposit will always succeed even if the admin + spikes fees from 0% to 10% between sign and execute. Use these + helpers (or compute equivalent caps yourself) on every + deposit path. + +

    Canonical versions

    This table is sourced directly from the SDK's{' '}