diff --git a/apps/investor-tokenization/src/app/layout.tsx b/apps/investor-tokenization/src/app/layout.tsx index 7cf9c4c..dfcfcc9 100644 --- a/apps/investor-tokenization/src/app/layout.tsx +++ b/apps/investor-tokenization/src/app/layout.tsx @@ -37,11 +37,11 @@ export default function RootLayout({ children: ReactNode; }>) { return ( - + diff --git a/apps/investor-tokenization/src/app/roi/layout.tsx b/apps/investor-tokenization/src/app/roi/layout.tsx new file mode 100644 index 0000000..a078208 --- /dev/null +++ b/apps/investor-tokenization/src/app/roi/layout.tsx @@ -0,0 +1,6 @@ +import { RoiDashboardShell } from "@/features/roi/roi-dashboard-shell"; +import { ReactNode } from "react"; + +export default function RoiLayout({ children }: { children: ReactNode }) { + return {children}; +} diff --git a/apps/investor-tokenization/src/app/roi/page.tsx b/apps/investor-tokenization/src/app/roi/page.tsx new file mode 100644 index 0000000..998cb1b --- /dev/null +++ b/apps/investor-tokenization/src/app/roi/page.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useState, useMemo } from "react"; +import { RoiHeader } from "@/features/roi/components/roi-header"; +import { CampaignToolbar } from "@/features/roi/components/campaign-toolbar"; +import { CampaignList } from "@/features/roi/components/campaign-list"; +import { mockCampaigns } from "@/features/roi/data/mock-campaigns"; + +export default function RoiPage() { + const [search, setSearch] = useState(""); + const [filter, setFilter] = useState("all"); + + const filteredCampaigns = useMemo(() => { + let list = mockCampaigns; + if (search.trim()) { + const q = search.toLowerCase(); + list = list.filter( + (c) => + c.title.toLowerCase().includes(q) || + c.description.toLowerCase().includes(q) + ); + } + if (filter !== "all") { + list = list.filter((c) => c.status.toLowerCase() === filter); + } + return list; + }, [search, filter]); + + const handleClaimRoi = (campaignId: string) => { + console.log("Claim ROI:", campaignId); + }; + + return ( +
+ + + +
+ ); +} diff --git a/apps/investor-tokenization/src/components/shared/Navbar.tsx b/apps/investor-tokenization/src/components/shared/Navbar.tsx index c5b2fd8..1da5e52 100644 --- a/apps/investor-tokenization/src/components/shared/Navbar.tsx +++ b/apps/investor-tokenization/src/components/shared/Navbar.tsx @@ -1,7 +1,7 @@ "use client"; import { FloatingDock } from "@tokenization/ui/floating-dock"; -import { CircleDollarSign, SquaresExclude, Wallet } from "lucide-react"; +import { CircleDollarSign, SquaresExclude, TrendingUp, Wallet } from "lucide-react"; import { useEffect, useState } from "react"; export function FloatingDockDemo() { @@ -20,6 +20,13 @@ export function FloatingDockDemo() { ), href: "/investments", }, + { + title: "ROI", + icon: ( + + ), + href: "/roi", + }, { title: "Claim ROI", icon: ( diff --git a/apps/investor-tokenization/src/features/roi/components/campaign-card.tsx b/apps/investor-tokenization/src/features/roi/components/campaign-card.tsx new file mode 100644 index 0000000..9926172 --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/campaign-card.tsx @@ -0,0 +1,62 @@ +import { + Card, + CardContent, + CardHeader, +} from "@tokenization/ui/card"; +import type { Campaign } from "../types/campaign.types"; +import { CampaignStatusBadge } from "./campaign-status-badge"; +import { ClaimRoiButton } from "./claim-roi-button"; + +type CampaignCardProps = { + campaign: Campaign; + onClaimRoi?: (campaignId: string) => void; +}; + +function formatMinInvest(cents: number, currency: string): string { + const value = cents / 100; + return new Intl.NumberFormat("en-US", { + style: "currency", + currency, + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }).format(value); +} + +export function CampaignCard({ campaign, onClaimRoi }: CampaignCardProps) { + return ( + + +
+

+ {campaign.title} +

+ +
+

+ {campaign.description} +

+
+ +
+
+

+ Loans completed +

+

+ {campaign.loansCompleted} +

+
+
+

+ Min. invest +

+

+ {formatMinInvest(campaign.minInvestCents, campaign.currency)} +

+
+
+ +
+
+ ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx b/apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx new file mode 100644 index 0000000..968d9bc --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@tokenization/ui/select"; +import { cn } from "@/lib/utils"; + +const filterOptions = [ + { value: "all", label: "All Campaigns" }, + { value: "ready", label: "Ready" }, + { value: "pending", label: "Pending" }, + { value: "closed", label: "Closed" }, +]; + +type CampaignFilterProps = { + value?: string; + onValueChange?: (value: string) => void; + className?: string; +}; + +export function CampaignFilter({ + value = "all", + onValueChange, + className, +}: CampaignFilterProps) { + return ( + + ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/campaign-list.tsx b/apps/investor-tokenization/src/features/roi/components/campaign-list.tsx new file mode 100644 index 0000000..73017b6 --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/campaign-list.tsx @@ -0,0 +1,19 @@ +import type { Campaign } from "../types/campaign.types"; +import { CampaignCard } from "./campaign-card"; + +type CampaignListProps = { + campaigns: Campaign[]; + onClaimRoi?: (campaignId: string) => void; +}; + +export function CampaignList({ campaigns, onClaimRoi }: CampaignListProps) { + return ( + + ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/campaign-search.tsx b/apps/investor-tokenization/src/features/roi/components/campaign-search.tsx new file mode 100644 index 0000000..979358e --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/campaign-search.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { Input } from "@tokenization/ui/input"; +import { Search } from "lucide-react"; +import { cn } from "@/lib/utils"; + +type CampaignSearchProps = { + value?: string; + onChange?: (value: string) => void; + placeholder?: string; + className?: string; +}; + +export function CampaignSearch({ + value, + onChange, + placeholder = "Search campaigns...", + className, +}: CampaignSearchProps) { + return ( +
+ + onChange?.(e.target.value)} + placeholder={placeholder} + className="pl-9 h-9 rounded-lg border-input bg-background text-sm" + aria-label="Search campaigns" + /> +
+ ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx b/apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx new file mode 100644 index 0000000..28e4fbe --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx @@ -0,0 +1,22 @@ +import { Badge } from "@tokenization/ui/badge"; +import { Check } from "lucide-react"; +import { cn } from "@/lib/utils"; + +type CampaignStatusBadgeProps = { + status: string; + className?: string; +}; + +export function CampaignStatusBadge({ status, className }: CampaignStatusBadgeProps) { + return ( + + + {status} + + ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/campaign-toolbar.tsx b/apps/investor-tokenization/src/features/roi/components/campaign-toolbar.tsx new file mode 100644 index 0000000..4be366d --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/campaign-toolbar.tsx @@ -0,0 +1,17 @@ +import { CampaignFilter } from "./campaign-filter"; + +type CampaignToolbarProps = { + filterValue?: string; + onFilterChange?: (value: string) => void; +}; + +export function CampaignToolbar({ + filterValue = "all", + onFilterChange, +}: CampaignToolbarProps) { + return ( +
+ +
+ ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/claim-roi-button.tsx b/apps/investor-tokenization/src/features/roi/components/claim-roi-button.tsx new file mode 100644 index 0000000..c82459e --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/claim-roi-button.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { Button } from "@tokenization/ui/button"; +import { FileText } from "lucide-react"; +import { cn } from "@/lib/utils"; + +type ClaimRoiButtonProps = { + campaignId: string; + className?: string; + onClick?: (campaignId: string) => void; +}; + +export function ClaimRoiButton({ + campaignId, + className, + onClick, +}: ClaimRoiButtonProps) { + return ( + + ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/investor-profile-card.tsx b/apps/investor-tokenization/src/features/roi/components/investor-profile-card.tsx new file mode 100644 index 0000000..802f4bf --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/investor-profile-card.tsx @@ -0,0 +1,48 @@ +import { Avatar, AvatarFallback, AvatarImage } from "@tokenization/ui/avatar"; +import { cn } from "@/lib/utils"; + +type InvestorProfileCardProps = { + name?: string; + avatarUrl?: string | null; + label?: string; + className?: string; +}; + +const defaultName = "Investor"; +const defaultLabel = "Investor"; + +export function InvestorProfileCard({ + name = defaultName, + avatarUrl, + label = defaultLabel, + className, +}: InvestorProfileCardProps) { + const initials = name + .split(" ") + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2); + + return ( +
+ + {avatarUrl ? ( + + ) : null} + + {initials} + + +
+

{name}

+

{label}

+
+
+ ); +} diff --git a/apps/investor-tokenization/src/features/roi/components/roi-header.tsx b/apps/investor-tokenization/src/features/roi/components/roi-header.tsx new file mode 100644 index 0000000..6cd4b62 --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/components/roi-header.tsx @@ -0,0 +1,42 @@ +import { Bell } from "lucide-react"; +import { CampaignSearch } from "./campaign-search"; +import { cn } from "@/lib/utils"; + +type RoiHeaderProps = { + searchValue?: string; + onSearchChange?: (value: string) => void; + className?: string; +}; + +export function RoiHeader({ + searchValue, + onSearchChange, + className, +}: RoiHeaderProps) { + return ( +
+
+

+ Invest in Campaigns +

+

+ Browse and support local entrepreneurship projects. +

+
+
+ + +
+
+ ); +} diff --git a/apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts b/apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts new file mode 100644 index 0000000..8f71ca8 --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts @@ -0,0 +1,34 @@ +import type { Campaign } from "../types/campaign.types"; + +export const mockCampaigns: Campaign[] = [ + { + id: "1", + title: "Coffee Producers Cooperative", + description: + "Expanding sustainable harvest infrastructure in Antioquia. This project aims to implement water-saving processing systems for 50 local families.", + status: "READY", + loansCompleted: 10, + minInvestCents: 25000, + currency: "USD", + }, + { + id: "2", + title: "Artisan Ceramic Collective", + description: + "Supporting traditional pottery techniques and new kiln installations. The collective brings together 30 artisans from the region to scale production and reach new markets.", + status: "READY", + loansCompleted: 8, + minInvestCents: 10000, + currency: "USD", + }, + { + id: "3", + title: "Urban Agriculture Network", + description: + "Rooftop and community garden expansion in Medellín. This initiative creates green jobs and improves food security through urban farming training and shared infrastructure.", + status: "READY", + loansCompleted: 12, + minInvestCents: 50000, + currency: "USD", + }, +]; diff --git a/apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx b/apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx new file mode 100644 index 0000000..88821e6 --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx @@ -0,0 +1,55 @@ +"use client"; + +import Image from "next/image"; +import { Megaphone, TrendingUp } from "lucide-react"; +import { ReactNode } from "react"; +import { + SidebarProvider, + SidebarInset, +} from "@tokenization/ui/sidebar"; +import { AppSidebar } from "@tokenization/ui/app-sidebar"; +import { SidebarWalletButton } from "@tokenization/ui/sidebar-wallet-button"; + +const ROI_NAV_ITEMS = [ + { href: "/", label: "Manage Campaigns", icon: Megaphone }, + { href: "/roi", label: "ROI", icon: TrendingUp }, +]; + +type RoiDashboardShellProps = { + children: ReactNode; +}; + +export function RoiDashboardShell({ children }: RoiDashboardShellProps) { + return ( +
+ + + ), + }} + footerContent={ +
+ +
+ } + /> + +
+ {children} +
+
+
+
+ ); +} diff --git a/apps/investor-tokenization/src/features/roi/types/campaign.types.ts b/apps/investor-tokenization/src/features/roi/types/campaign.types.ts new file mode 100644 index 0000000..6993e61 --- /dev/null +++ b/apps/investor-tokenization/src/features/roi/types/campaign.types.ts @@ -0,0 +1,11 @@ +export type CampaignStatus = "READY" | "PENDING" | "CLOSED"; + +export type Campaign = { + id: string; + title: string; + description: string; + status: CampaignStatus; + loansCompleted: number; + minInvestCents: number; + currency: string; +}; diff --git a/docs/PLAN_WALLET_KIT.md b/docs/PLAN_WALLET_KIT.md new file mode 100644 index 0000000..48e7ad8 --- /dev/null +++ b/docs/PLAN_WALLET_KIT.md @@ -0,0 +1,152 @@ +# Plan: Integrar el block Wallet Kit de Trustless Work + +Referencia: [Wallet Kit | Trustless Work Blocks](https://blocks.trustlesswork.com/blocks/wallet-kit) + +--- + +## 1. Objetivo + +Sustituir la implementación actual del wallet (custom en `packages/tw-blocks-shared/src/wallet-kit/`) por el **block oficial Wallet Kit** de Trustless Work, que ofrece: + +- **Wallet** (estado/contexto) +- **StellarProvider** (Stellar Wallets Kit) +- **Connect Button** (botón de conectar wallet) + +Requisitos del block (según la documentación): + +- Necesario para todos los blocks de escrow. +- Provee wallet provider, validadores y botón de conexión con Stellar Wallets Kit. +- Si no se usa el contexto de escrow, el payload debe proporcionarse por otro medio. +- Debe añadirse la trustline USDC en la wallet para que las interacciones con escrow funcionen. + +--- + +## 2. Estado actual en el monorepo + +### 2.1 Implementación actual (custom) + +| Ubicación | Contenido | +|-----------|-----------| +| `packages/tw-blocks-shared/src/wallet-kit/WalletProvider.tsx` | Contexto (walletAddress, walletName, setWalletInfo, clearWalletInfo). | +| `packages/tw-blocks-shared/src/wallet-kit/WalletButtons.tsx` | Botón connect/disconnect + popover (usa `useWalletContext`, `useWallet`). | +| `packages/tw-blocks-shared/src/wallet-kit/useWallet.ts` | handleConnect / handleDisconnect usando `getKit()` (Stellar Wallets Kit). | +| `packages/tw-blocks-shared/src/wallet-kit/wallet-kit.ts` | `getKit()` (lazy init de StellarWalletsKit) y `signTransaction()`. | +| `packages/tw-blocks-shared/src/wallet-kit/validators.ts` | Validadores (p. ej. `isValidWallet`) usados en escrows. | +| `packages/tw-blocks-shared/src/wallet-kit/trustlines.ts` | Opciones de trustline para formularios de escrow. | + +Dependencia directa: `@creit.tech/stellar-wallets-kit` (en root `package.json`). + +### 2.2 Uso actual del wallet + +- **Apps** + - **investor-tokenization**: `app/layout.tsx` → `WalletProvider`; `Header.tsx` y `roi-dashboard-shell.tsx` → `WalletButton` / `SidebarWalletButton`. + - **backoffice-tokenization**: `app/layout.tsx` → `WalletProvider`; `Header.tsx` → `WalletButton`. +- **Packages** + - **packages/ui**: `sidebar-wallet-button.tsx` → `WalletButton` de tw-blocks-shared. + - **packages/tw-blocks-shared**: Todos los bloques de escrow (fund, approve, release, dispute, initialize, update, withdraw, resolve, etc.) usan `useWalletContext()` y muchos usan `signTransaction`, `validators`, `trustlines`. + +--- + +## 3. Pasos del plan (no ejecutar hasta aprobar) + +### Fase 1: Instalación y revisión del block + +0. **Inicialización obligatoria (requerido por la CLI)** + - Antes de añadir cualquier block, hay que ejecutar **una vez**: + ```bash + npx trustless-work init + ``` + - **Importante:** Usar `npx trustless-work init` (no `trustless-work init`); sin `npx` el comando no se encuentra. + - Este paso instala dependencias y crea `.twblocks.json` (configuración uiBase). Si no se hace, al ejecutar `npx trustless-work add wallet-kit` aparece: + ``` + ❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase). + ``` + +1. **Instalar el block** + - En la raíz del monorepo (o en la app que vaya a consumir el block primero): + `npx trustless-work add wallet-kit` + - Revisar qué archivos/paquetes añade (p. ej. en `packages/` o en una app) y cómo exporta: + - Provider del wallet (equivalente a `WalletProvider`) + - StellarProvider (equivalente a la configuración actual de Stellar Wallets Kit) + - Connect Button (equivalente a `WalletButton`) + +2. **Revisar dependencias del block** + - Consultar la [sección de dependencias](https://blocks.trustlesswork.com/get-started/dependencies) para wallet-kit. + - Comprobar compatibilidad con React 19, Next.js 16 y con `@trustless-work/escrow` ya usado en el repo. + +3. **Documentar la API del block** + - Nombres de componentes (provider, botón). + - Si el context se accede por un hook (p. ej. `useWalletContext` o similar). + - Si el block expone algo equivalente a `signTransaction` o si las apps/blocks deben seguir usando la implementación actual para firmar. + +### Fase 2: Estrategia de migración + +4. **Decidir enfoque** + - **Opción A – Sustitución directa**: Reemplazar en `tw-blocks-shared` el uso de `WalletProvider`/`WalletButton`/`useWallet` por los del block; mantener en tw-blocks-shared solo lo que el block no cubra (p. ej. `signTransaction`, validators, trustlines) y re-exportar lo que venga del block. + - **Opción B – Capa de adaptación**: Introducir en `tw-blocks-shared` una capa que use el block por debajo y exponga la misma API actual (`WalletProvider`, `WalletButton`, `useWalletContext`), para que las apps y `packages/ui` no cambien imports. Útil si la API del block difiere de la actual. + +5. **Compatibilidad con escrow** + - Los bloques de escrow en tw-blocks-shared necesitan: + - Contexto de wallet (dirección/nombre) → debe venir del block o del adaptador. + - Firma de transacciones → si el block no expone `signTransaction`, mantener `wallet-kit.ts` (o equivalente) y que use el kit que provea el StellarProvider del block. + - Validadores y trustlines pueden seguir viviendo en tw-blocks-shared salvo que el block traiga los suyos. + +### Fase 3: Cambios por capa + +6. **Layouts de las apps** + - Sustituir en `investor-tokenization` y `backoffice-tokenization`: + - Import de `WalletProvider` (y si aplica StellarProvider) por el provider del block (o por el re-export desde tw-blocks-shared). + - Mantener el orden de providers según indique la doc del block (p. ej. StellarProvider > WalletProvider u otro). + +7. **Header y sidebar** + - **Header** (investor y backoffice): Sustituir `WalletButton` por el Connect Button del block (o por el re-export desde tw-blocks-shared). + - **packages/ui**: En `sidebar-wallet-button.tsx`, usar el Connect Button del block en lugar de `WalletButton` de tw-blocks-shared (o seguir usando tw-blocks-shared si ahí se re-exporta el del block). + +8. **packages/tw-blocks-shared** + - Si se elige **Opción A**: Eliminar o deprecar `WalletProvider.tsx`, `WalletButtons.tsx`, `useWallet.ts` y la parte de `wallet-kit.ts` que solo inicialice el kit; re-exportar provider y botón del block; mantener `signTransaction`, `validators`, `trustlines` y cualquier hook que los use. + - Si se elige **Opción B**: Crear módulos que importen el block y expongan `WalletProvider`, `WalletButton`, `useWalletContext` con la misma firma que hoy; el resto de tw-blocks-shared sigue igual. + - Actualizar todos los archivos que importan de `wallet-kit/WalletProvider` o `wallet-kit/WalletButtons` para que apunten al nuevo origen (block o capa de adaptación). + +9. **Dependencias** + - Si el block incluye o exige `@creit.tech/stellar-wallets-kit`, dejar que el block lo gestione; si no, mantener la dependencia solo donde se siga usando (p. ej. `signTransaction`). + - Comprobar que `@trustless-work/blocks` (ya en el repo) sea la versión esperada por el wallet-kit block. + +### Fase 4: Pruebas y rollback + +10. **Pruebas** + - Ambas apps arrancan sin error (investor-tokenization, backoffice-tokenization). + - Conectar/desconectar wallet en Header y en sidebar (página ROI). + - Flujos de escrow que usen wallet (inicializar, fund, approve, release, etc.) siguen funcionando. + - Si aplica, comprobar que la trustline USDC esté presente o que se guíe al usuario a añadirla. + +11. **Rollback** + - Dejar la implementación actual (carpeta `wallet-kit/` y sus imports) intacta hasta confirmar que el block funciona en dev; usar feature flag o rama para cambiar entre “wallet custom” y “wallet block” si se desea. + +--- + +## 4. Resumen de archivos a tocar (estimado) + +| Área | Archivos | +|------|----------| +| Instalación | Salida de `npx trustless-work add wallet-kit` (por definir). | +| Apps | `investor-tokenization/src/app/layout.tsx`, `Header.tsx`; `backoffice-tokenization/src/app/layout.tsx`, `Header.tsx`. | +| ROI | `investor-tokenization/src/features/roi/roi-dashboard-shell.tsx` (ya usa SidebarWalletButton). | +| UI shared | `packages/ui/src/sidebar-wallet-button.tsx`. | +| tw-blocks-shared | `packages/tw-blocks-shared/src/wallet-kit/*` (refactor o re-export); todos los escrows y tanstack que importen wallet (lista en sección 2.2). | +| Root | `package.json` (dependencias si el block las añade o modifica). | + +--- + +## 5. Orden recomendado de ejecución + +1. Ejecutar `npx trustless-work add wallet-kit` y documentar cambios. +2. Revisar dependencias y documentación del block. +3. Elegir Opción A o B y aplicar cambios en tw-blocks-shared. +4. Actualizar layouts y Headers de ambas apps. +5. Actualizar `sidebar-wallet-button.tsx`. +6. Ejecutar pruebas manuales y de integración. +7. Eliminar o deprecar código custom del wallet solo después de validar. + +--- + +*Documento solo de planificación. No ejecutar pasos hasta aprobación.* diff --git a/package-lock.json b/package-lock.json index 8f33407..b1d098e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5467,6 +5467,28 @@ "license": "Apache-2.0", "peer": true }, + "node_modules/@near-js/providers/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@near-js/signers": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@near-js/signers/-/signers-0.2.2.tgz", @@ -13436,6 +13458,20 @@ "license": "MIT", "peer": true }, + "node_modules/bufferutil": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", + "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/bundle-require": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", @@ -24946,6 +24982,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",