From ab1126a93d535cfb5e6867478d9d0bcfb0f417e5 Mon Sep 17 00:00:00 2001 From: rounak-Rao Date: Sat, 7 Feb 2026 03:41:35 -0800 Subject: [PATCH 1/5] Integrate Tenderly Virtual TestNet with automated wallet management. Agents now automatically receive encrypted, funded wallets (10k USDC + 1 ETH) on creation via Tenderly API. Wallet credentials are encrypted and stored in PostgreSQL, with TradeTool loading credentials from the database using performance caching. Created wallet_utils.py for generation/encryption and tenderly.py for funding automation. Modified agent.py, database_tool.py, and trade_tool.py to support database-backed credential management. --- backend/app/agents/onchain/swap.py | 26 +--- backend/app/agents/onchain/tenderly.py | 173 +++++++++++++++++++++ backend/app/agents/onchain/wallet_utils.py | 103 ++++++++++++ backend/app/agents/tools/database_tool.py | 48 ++++++ backend/app/agents/tools/trade_tool.py | 139 ++++++++++++++++- backend/app/api/routers/agent.py | 45 +++++- backend/test_tradetool.py | 100 ++++++++++++ backend/test_wallet_funding.py | 55 +++++++ 8 files changed, 660 insertions(+), 29 deletions(-) create mode 100644 backend/app/agents/onchain/tenderly.py create mode 100644 backend/app/agents/onchain/wallet_utils.py create mode 100644 backend/test_tradetool.py create mode 100644 backend/test_wallet_funding.py diff --git a/backend/app/agents/onchain/swap.py b/backend/app/agents/onchain/swap.py index 25d3ffc..8570128 100644 --- a/backend/app/agents/onchain/swap.py +++ b/backend/app/agents/onchain/swap.py @@ -38,7 +38,8 @@ def execute_swap( - agent_id: str, + wallet_address: str, + wallet_private_key: str, from_token: Literal["USDC", "WETH", "CBBTC"], to_token: Literal["USDC", "WETH", "CBBTC"], amount: float, @@ -48,7 +49,8 @@ def execute_swap( Execute a swap using the uniswap-python library. Args: - agent_id: Agent identifier (e.g., "agent_1") + wallet_address: Wallet address to execute swap from + wallet_private_key: Private key for signing (plain, not encrypted) from_token: Source token symbol to_token: Destination token symbol amount: Amount to swap (in token units, not wei) @@ -62,22 +64,8 @@ def execute_swap( Raises: ValueError: If swap fails or invalid parameters provided """ - # Get agent private key from env - private_key_var = f"{agent_id.upper()}_PRIVATE_KEY" - agent_private_key = os.getenv(private_key_var) - - if not agent_private_key: - raise ValueError(f"Missing environment variable: {private_key_var}") - - # Get agent address - address_var = f"{agent_id.upper()}_ADDRESS" - agent_address = os.getenv(address_var) - - if not agent_address: - raise ValueError(f"Missing environment variable: {address_var}") - # Get RPC URL - rpc_url = os.getenv("RPC_LOCAL", "http://127.0.0.1:8545") + rpc_url = os.getenv("RPC_URL") # Validate tokens from_token = from_token.upper() @@ -103,8 +91,8 @@ def execute_swap( try: # Initialize Uniswap instance with V3 uniswap = Uniswap( - address=agent_address, - private_key=agent_private_key, + address=wallet_address, + private_key=wallet_private_key, version=3, provider=rpc_url, web3=None # Let the library create the web3 instance diff --git a/backend/app/agents/onchain/tenderly.py b/backend/app/agents/onchain/tenderly.py new file mode 100644 index 0000000..8a02e1f --- /dev/null +++ b/backend/app/agents/onchain/tenderly.py @@ -0,0 +1,173 @@ +""" +Tenderly RPC integration for Virtual TestNet wallet funding. + +Uses Tenderly's JSON-RPC API to fund wallets with ETH and ERC-20 tokens. +""" + +import os +import requests + + +# Base mainnet token addresses +USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" +WETH_ADDRESS = "0x4200000000000000000000000000000000000006" +CBBTC_ADDRESS = "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf" + + +def get_rpc_url() -> str: + """Get Tenderly Virtual TestNet RPC URL from environment.""" + rpc_url = os.getenv("RPC_URL") + if not rpc_url: + raise ValueError("RPC_URL not set in environment") + return rpc_url + + +def fund_wallet_with_eth(wallet_address: str, amount_eth: float = 10.0) -> dict: + """ + Fund a wallet with native ETH on Tenderly Virtual TestNet. + + Args: + wallet_address: The wallet address to fund (0x...) + amount_eth: Amount of ETH to send (default 10.0) + + Returns: + Response dict with success status + """ + rpc_url = get_rpc_url() + + # Convert ETH to Wei (1 ETH = 10^18 Wei) + amount_wei = int(amount_eth * 10**18) + + payload = { + "jsonrpc": "2.0", + "method": "tenderly_setBalance", + "params": [[wallet_address], hex(amount_wei)], + "id": "1" + } + + response = requests.post(rpc_url, json=payload, headers={"Content-Type": "application/json"}) + + if response.status_code == 200: + print(f"Funded {wallet_address} with {amount_eth} ETH") + return {"success": True, "amount": amount_eth, "address": wallet_address} + else: + print(f"Failed to fund wallet: {response.status_code} - {response.text}") + response.raise_for_status() + + +def fund_wallet_with_token( + wallet_address: str, + token_address: str, + amount: float, + decimals: int = 6 +) -> dict: + """ + Fund a wallet with ERC-20 tokens on Tenderly Virtual TestNet. + + Args: + wallet_address: The wallet address to fund (0x...) + token_address: The token contract address (0x...) + amount: Amount of tokens (human readable, e.g., 1000 for 1000 USDC) + decimals: Token decimals (USDC=6, WETH=18, CBBTC=8) + + Returns: + Response dict with success status + """ + rpc_url = get_rpc_url() + + # Convert amount to smallest unit (e.g., 1000 USDC -> 1000000000 for 6 decimals) + amount_smallest_unit = int(amount * 10**decimals) + + payload = { + "jsonrpc": "2.0", + "method": "tenderly_setErc20Balance", + "params": [token_address, wallet_address, hex(amount_smallest_unit)], + "id": "1" + } + + response = requests.post(rpc_url, json=payload, headers={"Content-Type": "application/json"}) + + if response.status_code == 200: + print(f"Funded {wallet_address} with {amount} tokens") + return { + "success": True, + "amount": amount, + "token": token_address, + "recipient": wallet_address + } + else: + print(f"Failed to fund wallet with tokens: {response.status_code} - {response.text}") + response.raise_for_status() + + +def fund_wallet_with_usdc(wallet_address: str, amount_usdc: float = 10000.0) -> dict: + """ + Fund a wallet with USDC on Tenderly Virtual TestNet. + + Args: + wallet_address: The wallet address to fund (0x...) + amount_usdc: Amount of USDC (default 10,000) + + Returns: + Response dict with success status + """ + return fund_wallet_with_token( + wallet_address=wallet_address, + token_address=USDC_ADDRESS, + amount=amount_usdc, + decimals=6 # USDC has 6 decimals + ) + + +def reset_wallet_balance(wallet_address: str, usdc_amount: float = 10000.0, eth_amount: float = 1.0) -> dict: + """ + Reset a wallet's balance back to initial amounts. + + Useful for testing - resets both ETH and USDC to starting values. + + Args: + wallet_address: The wallet address to reset (0x...) + usdc_amount: USDC amount to reset to (default 10,000) + eth_amount: ETH amount to reset to (default 1.0) + + Returns: + Dict with reset results + """ + return setup_agent_wallet(wallet_address, usdc_amount, eth_amount) + + +def setup_agent_wallet(wallet_address: str, initial_usdc: float = 10000.0, initial_eth: float = 1.0) -> dict: + """ + Complete wallet setup: fund with both ETH (for gas) and USDC (for trading). + + Args: + wallet_address: The wallet address to setup (0x...) + initial_usdc: Starting USDC balance (default 10,000) + initial_eth: Starting ETH balance for gas (default 1.0) + + Returns: + Dict with funding results + """ + print(f"Setting up wallet: {wallet_address}") + + try: + # Fund with ETH for gas fees + eth_result = fund_wallet_with_eth(wallet_address, initial_eth) + + # Fund with USDC for trading + usdc_result = fund_wallet_with_usdc(wallet_address, initial_usdc) + + print(f"Wallet setup complete!") + print(f" ETH: {initial_eth}") + print(f" USDC: {initial_usdc}") + + return { + "success": True, + "wallet": wallet_address, + "eth_funded": eth_result, + "usdc_funded": usdc_result + } + + except Exception as e: + print(f"Failed to setup wallet: {str(e)}") + raise diff --git a/backend/app/agents/onchain/wallet_utils.py b/backend/app/agents/onchain/wallet_utils.py new file mode 100644 index 0000000..253c0d8 --- /dev/null +++ b/backend/app/agents/onchain/wallet_utils.py @@ -0,0 +1,103 @@ +""" +Wallet utility functions for agent wallet management. + +Handles wallet generation, encryption, and transaction signing. +""" + +from eth_account import Account +from cryptography.fernet import Fernet +import os +from typing import Dict + + +def generate_wallet() -> Dict[str, str]: + """ + Generate a new Ethereum wallet with private key and address. + + Returns: + Dict with 'address' and 'private_key' keys + """ + account = Account.create() + + # Get private key as bytes, then convert to hex (always 64 chars) + private_key_bytes = account.key + private_key_hex = private_key_bytes.hex() + + return { + "address": account.address, + "private_key": private_key_hex + } + + +def get_encryption_key() -> bytes: + """ + Get the Fernet encryption key from environment. + + Raises: + ValueError: If WALLET_ENCRYPTION_KEY not set in environment + + Returns: + Encryption key as bytes + """ + encryption_key = os.getenv("WALLET_ENCRYPTION_KEY") + + if not encryption_key: + new_key = Fernet.generate_key() + print(f"\n⚠️ WALLET_ENCRYPTION_KEY not found!") + print(f"Add this to your .env file:") + print(f"WALLET_ENCRYPTION_KEY={new_key.decode()}\n") + raise ValueError("WALLET_ENCRYPTION_KEY not set in environment") + + return encryption_key.encode() + + +def encrypt_private_key(private_key: str) -> str: + """ + Encrypt a private key for secure database storage. + + Args: + private_key: Plain private key as hex string + + Returns: + Encrypted private key as string + """ + key = get_encryption_key() + cipher = Fernet(key) + encrypted = cipher.encrypt(private_key.encode()) + return encrypted.decode() + + +def decrypt_private_key(encrypted_private_key: str) -> str: + """ + Decrypt a private key from database storage. + + Args: + encrypted_private_key: Encrypted private key from database + + Returns: + Plain private key as hex string + """ + key = get_encryption_key() + cipher = Fernet(key) + decrypted = cipher.decrypt(encrypted_private_key.encode()) + return decrypted.decode() + + +def private_key_to_address(private_key: str) -> str: + """ + Derive Ethereum address from a private key. + + Args: + private_key: Private key as hex string (with or without 0x prefix) + + Returns: + Ethereum address (0x...) + """ + # Add 0x prefix if not present + if not private_key.startswith("0x"): + private_key = "0x" + private_key + + account = Account.from_key(private_key) + return account.address + + diff --git a/backend/app/agents/tools/database_tool.py b/backend/app/agents/tools/database_tool.py index 195b7a8..8313573 100644 --- a/backend/app/agents/tools/database_tool.py +++ b/backend/app/agents/tools/database_tool.py @@ -355,6 +355,54 @@ async def get_agent_by_name(self, agent_name: str) -> Optional[Agent]: logger.error(f"Failed to get agent by name: {e}") raise + async def get_agent_wallet_credentials( + self, agent_uuid: UUID + ) -> Dict[str, str]: + """ + Load agent's wallet credentials from database. + + Args: + agent_uuid: Agent UUID + + Returns: + Dict with 'wallet_address' and 'encrypted_private_key' + + Raises: + ValueError: If agent not found or wallet credentials missing + """ + try: + stmt = select(Agent).where(Agent.id == agent_uuid) + result = await self.session.execute(stmt) + agent = result.scalar_one_or_none() + + if not agent: + raise ValueError(f"Agent not found: {agent_uuid}") + + # Extract wallet credentials from stats + wallet_address = agent.stats.get("wallet_address") + encrypted_private_key = agent.stats.get("encrypted_private_key") + + if not wallet_address or not encrypted_private_key: + raise ValueError( + f"Agent {agent_uuid} missing wallet credentials in stats. " + f"Has wallet_address: {bool(wallet_address)}, " + f"Has encrypted_private_key: {bool(encrypted_private_key)}" + ) + + logger.debug( + f"Loaded wallet credentials for agent={agent_uuid}, " + f"wallet={wallet_address}" + ) + + return { + "wallet_address": wallet_address, + "encrypted_private_key": encrypted_private_key, + } + + except Exception as e: + logger.error(f"Failed to load wallet credentials for agent={agent_uuid}: {e}") + raise + async def create_agent_if_not_exists( self, agent_name: str, diff --git a/backend/app/agents/tools/trade_tool.py b/backend/app/agents/tools/trade_tool.py index cce2605..b3a4c46 100644 --- a/backend/app/agents/tools/trade_tool.py +++ b/backend/app/agents/tools/trade_tool.py @@ -1,11 +1,12 @@ -# DEPECRATED: This module is deprecated and will be removed in future versions. import time import logging from datetime import datetime, timezone -from typing import List +from typing import List, Optional +from uuid import UUID from ..data_classes import Trade from ..onchain.ts_swap_wrapper import execute_ts_swap +from ..onchain.wallet_utils import decrypt_private_key logger = logging.getLogger(__name__) @@ -17,13 +18,112 @@ class TradeToolError(Exception): class TradeTool: - """Execute on-chain trades and helper calculations.""" + """ + Execute on-chain trades and helper calculations. - def __init__(self, agent_id: str): + Loads wallet credentials from database on first trade execution, + caches decrypted private key in memory for performance. + """ + + def __init__( + self, + agent_id: str, + agent_uuid: Optional[UUID] = None, + database_tool: Optional["DatabaseTool"] = None, + ): + """ + Initialize TradeTool. + + Args: + agent_id: Agent identifier string (for logging) + agent_uuid: Agent UUID for database lookups (required for real trading) + database_tool: DatabaseTool instance for loading wallet credentials + """ self.agent_id = agent_id - logger.info(f"TradeTool initialized for agent_id={agent_id}") + self.agent_uuid = agent_uuid + self.database_tool = database_tool + self._private_key_cache: Optional[str] = None + self._wallet_address_cache: Optional[str] = None + + logger.info( + f"TradeTool initialized for agent_id={agent_id}, " + f"agent_uuid={agent_uuid}, " + f"database_backed={database_tool is not None}" + ) + + async def _load_private_key_from_db(self) -> str: + """ + Load and decrypt agent's private key from database. + + Uses caching to avoid repeated DB queries and decryption. + + Returns: + Decrypted private key as hex string + + Raises: + TradeToolError: If database_tool or agent_uuid not provided, or if credentials missing + """ + # Check cache first + if self._private_key_cache is not None: + logger.debug( + f"Using cached private key for agent {self.agent_id} " + f"(wallet: {self._wallet_address_cache})" + ) + return self._private_key_cache + + # Validate dependencies + if self.database_tool is None or self.agent_uuid is None: + raise TradeToolError( + f"Cannot load private key: TradeTool not configured with database access. " + f"agent_uuid={self.agent_uuid}, database_tool={self.database_tool is not None}" + ) + + # Load encrypted credentials from database + logger.info(f"Loading wallet credentials from database for agent {self.agent_id}") + try: + credentials = await self.database_tool.get_agent_wallet_credentials( + self.agent_uuid + ) + encrypted_key = credentials["encrypted_private_key"] + wallet_address = credentials["wallet_address"] + + logger.debug(f"Loaded credentials for wallet: {wallet_address}") - def execute_trade( + except Exception as e: + raise TradeToolError( + f"Failed to load wallet credentials from database: {str(e)}" + ) from e + + # Decrypt private key + try: + decrypted_key = decrypt_private_key(encrypted_key) + logger.debug(f"Successfully decrypted private key for agent {self.agent_id}") + + except Exception as e: + raise TradeToolError( + f"Failed to decrypt private key: {str(e)}" + ) from e + + # Cache for future trades + self._private_key_cache = decrypted_key + self._wallet_address_cache = wallet_address + logger.info( + f"Wallet credentials loaded and cached for agent {self.agent_id} " + f"(wallet: {wallet_address})" + ) + + return decrypted_key + + @property + def wallet_address(self) -> Optional[str]: + """ + Get the cached wallet address. + + Returns None if credentials haven't been loaded yet. + """ + return self._wallet_address_cache + + async def execute_trade( self, action: str, token: str, @@ -32,6 +132,25 @@ def execute_trade( confidence: float, summary: str, ) -> Trade: + """ + Execute an on-chain trade using Uniswap V3. + + Automatically loads agent's private key from database (with caching). + + Args: + action: "BUY" or "SELL" + token: Token symbol ("WETH", "CBBTC", "USDC") + qty: Quantity to trade + price: Target price (not used for on-chain trades) + confidence: Confidence score (0-1) + summary: Trade summary/reasoning + + Returns: + Trade object with transaction hash and actual execution details + + Raises: + TradeToolError: If trade execution fails + """ action = action.upper() if action not in ["BUY", "SELL"]: logger.error(f"Invalid action: {action}") @@ -54,8 +173,12 @@ def execute_trade( trade_id = int(time.time() * 1000) timestamp = datetime.now(timezone.utc) + # Load private key from database (uses cache if available) + private_key = await self._load_private_key_from_db() + logger.info( - f"Executing {action} trade: {qty} {token} for agent {self.agent_id}" + f"Executing {action} trade: {qty} {token} for agent {self.agent_id} " + f"(wallet: {self.wallet_address})" ) try: @@ -67,6 +190,7 @@ def execute_trade( to_token=token, amount=qty, slippage=50, + private_key=private_key, # Pass decrypted key directly ) amount_out_wei = int(swap_result["amount_out"]) actual_qty = amount_out_wei / (10 ** token_decimals[token]) @@ -82,6 +206,7 @@ def execute_trade( to_token="USDC", amount=qty, slippage=50, + private_key=private_key, # Pass decrypted key directly ) amount_out_wei = int(swap_result["amount_out"]) usdc_received = amount_out_wei / (10 ** token_decimals["USDC"]) diff --git a/backend/app/api/routers/agent.py b/backend/app/api/routers/agent.py index 59d1e32..b894602 100644 --- a/backend/app/api/routers/agent.py +++ b/backend/app/api/routers/agent.py @@ -2,11 +2,16 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from uuid import UUID +import logging from ...db.database import get_db from ...db.models import Agent from ...schemas.agent import AgentCreate, AgentUpdate, AgentResponse from ..deps import require_admin +from ...agents.onchain.wallet_utils import generate_wallet, encrypt_private_key +from ...agents.onchain.tenderly import setup_agent_wallet + +logger = logging.getLogger(__name__) router = APIRouter() @@ -46,8 +51,12 @@ async def create_agent( session: AsyncSession = Depends(get_db), admin: dict = Depends(require_admin), ): - """POST route for creating a new agent (admin only)""" - # Create Agent model from schema, excluding None values to use model defaults + """ + POST route for creating a new agent (admin only). + + Automatically generates an Ethereum wallet, encrypts the private key, + stores wallet data in the agent's stats, and funds the wallet via Tenderly. + """ agent_dict = agent_data.model_dump(exclude_none=True) # Ensure stats and memory have default values if not provided @@ -56,11 +65,41 @@ async def create_agent( if 'memory' not in agent_dict: agent_dict['memory'] = {} - agent = Agent(**agent_dict) + # Generate new Ethereum wallet for this agent + logger.info(f"Generating wallet for agent: {agent_dict.get('name', 'unnamed')}") + wallet = generate_wallet() + wallet_address = wallet['address'] + plain_private_key = wallet['private_key'] + + # Encrypt private key for secure database storage + logger.info(f"Encrypting private key for wallet: {wallet_address}") + encrypted_key = encrypt_private_key(plain_private_key) + # Store wallet credentials in agent stats + agent_dict['stats']['wallet_address'] = wallet_address + agent_dict['stats']['encrypted_private_key'] = encrypted_key + logger.info(f"Wallet credentials stored in agent stats") + + # Create agent with wallet data + agent = Agent(**agent_dict) session.add(agent) await session.commit() await session.refresh(agent) + logger.info(f"Agent created with ID: {agent.id}") + + # Fund wallet via Tenderly Virtual TestNet + try: + logger.info(f"Funding wallet via Tenderly: {wallet_address}") + setup_agent_wallet( + wallet_address=wallet_address, + initial_usdc=10000.0, # Start with 10k USDC + initial_eth=1.0 # 1 ETH for gas fees + ) + logger.info(f"Wallet funded successfully: {wallet_address}") + except Exception as e: + logger.error(f"Failed to fund wallet {wallet_address}: {str(e)}") + # Don't fail agent creation if funding fails - wallet can be funded later + return agent diff --git a/backend/test_tradetool.py b/backend/test_tradetool.py new file mode 100644 index 0000000..952ffa5 --- /dev/null +++ b/backend/test_tradetool.py @@ -0,0 +1,100 @@ +""" +Test TradeTool with Tenderly Virtual TestNet. + +This tests the full TradeTool class (BUY and SELL operations). +""" + +import os +os.environ['RPC_URL'] = "https://virtual.base.us-west.rpc.tenderly.co/5dab82d9-f35a-4d60-bced-0f6ba5293675" +os.environ['TEST_WALLET_ADDRESS'] = "0xF8C43b4C988E4E6a74DA80AFa8fBc5A40d4D77FD" +os.environ['TEST_WALLET_PRIVATE_KEY'] = "bb50c98e776cce4628a7977fca1f9e0fab8e15ca2ca696c70baa64c6a448616f" + +from app.agents.tools.trade_tool import TradeTool + + +def test_buy(): + """Test BUY operation.""" + print("=" * 60) + print("TEST 1: BUY 100 USDC worth of WETH") + print("=" * 60) + + tool = TradeTool(agent_id="test_wallet") + + try: + trade = tool.execute_trade( + action="BUY", + token="WETH", + qty=100, # 100 USDC + price=0, # Price not used for on-chain trades + confidence=0.8, + summary="Test buy WETH" + ) + + print("\n✅ BUY SUCCESS!") + print(f" TX Hash: {trade.tx_hash}") + print(f" Token: {trade.token}") + print(f" Quantity: {trade.qty}") + print(f" Price: {trade.price}") + return True + + except Exception as e: + print(f"\n❌ BUY FAILED: {str(e)}") + import traceback + traceback.print_exc() + return False + + +def test_sell(): + """Test SELL operation.""" + print("\n" + "=" * 60) + print("TEST 2: SELL 0.01 WETH for USDC") + print("=" * 60) + + tool = TradeTool(agent_id="test_wallet") + + try: + trade = tool.execute_trade( + action="SELL", + token="WETH", + qty=0.01, # 0.01 WETH + price=0, + confidence=0.8, + summary="Test sell WETH" + ) + + print("\n✅ SELL SUCCESS!") + print(f" TX Hash: {trade.tx_hash}") + print(f" Token: {trade.token}") + print(f" Quantity: {trade.qty}") + print(f" Price: {trade.price}") + return True + + except Exception as e: + print(f"\n❌ SELL FAILED: {str(e)}") + import traceback + traceback.print_exc() + return False + + +def main(): + print("\nTesting TradeTool with Tenderly Virtual TestNet\n") + + buy_works = test_buy() + sell_works = test_sell() + + print("\n" + "=" * 60) + print("RESULTS") + print("=" * 60) + print(f"BUY: {'✅ WORKS' if buy_works else '❌ FAILED'}") + print(f"SELL: {'✅ WORKS' if sell_works else '❌ FAILED'}") + + if buy_works and sell_works: + print("\n🎉 TradeTool is fully working with Tenderly!") + print("\nView transactions on Tenderly:") + print("https://dashboard.tenderly.co/rounak-rao/project/vnets") + else: + print("\n⚠️ Some tests failed - see errors above") + + +if __name__ == "__main__": + main() diff --git a/backend/test_wallet_funding.py b/backend/test_wallet_funding.py new file mode 100644 index 0000000..2a7c16d --- /dev/null +++ b/backend/test_wallet_funding.py @@ -0,0 +1,55 @@ +""" +Test wallet generation and Tenderly funding. +""" + +import os +import sys +from dotenv import load_dotenv + +load_dotenv() +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from app.agents.onchain.wallet_utils import ( + generate_wallet, + encrypt_private_key, + decrypt_private_key, + private_key_to_address +) +from app.agents.onchain.tenderly import setup_agent_wallet + + +def main(): + print("Generating new wallet...") + wallet = generate_wallet() + print(f"Address: {wallet['address']}") + + print("\nTesting encryption...") + encrypted = encrypt_private_key(wallet['private_key']) + decrypted = decrypt_private_key(encrypted) + assert decrypted == wallet['private_key'] + print("Encryption works") + + print("\nVerifying address derivation...") + derived_address = private_key_to_address(wallet['private_key']) + print(f"Original: {wallet['address']}") + print(f"Derived: {derived_address}") + assert derived_address == wallet['address'] + print("Address derivation works") + + print("\nFunding wallet via Tenderly (5000 USDC, 1 ETH)...") + result = setup_agent_wallet( + wallet_address=wallet['address'], + initial_usdc=5000.0, + initial_eth=1.0 + ) + + print(f"\nVerify on Tenderly Dashboard:") + print(f"https://dashboard.tenderly.co/{os.getenv('TENDERLY_ACCOUNT_SLUG')}/{os.getenv('TENDERLY_PROJECT_SLUG')}/vnets") + print(f"Search for wallet: {wallet['address']}") + print(f"\nSave for next test:") + print(f"WALLET_ADDRESS={wallet['address']}") + print(f"WALLET_PRIVATE_KEY={wallet['private_key']}") + + +if __name__ == "__main__": + main() From 2be001aa5c94fd381c7ece1fe00ab6311f68d400 Mon Sep 17 00:00:00 2001 From: Jacob Horne Date: Sat, 7 Feb 2026 12:06:18 -0800 Subject: [PATCH 2/5] refactored executor, ts_swap to use updated trade tool --- backend/app/agents/executor.py | 262 ++- backend/app/agents/onchain/ts_swap_wrapper.py | 14 +- backend/app/agents/tools/__init__.py | 11 +- backend/app/agents/tools/trade_tool.py | 6 +- backend/celerybeat-schedule | Bin 12288 -> 16384 bytes backend/requirements.txt | 2 +- frontend/package-lock.json | 1928 +++++++++++++---- 7 files changed, 1754 insertions(+), 469 deletions(-) diff --git a/backend/app/agents/executor.py b/backend/app/agents/executor.py index a6a230f..9dbd59a 100644 --- a/backend/app/agents/executor.py +++ b/backend/app/agents/executor.py @@ -3,7 +3,7 @@ This module implements a complete trading agent that extends BaseAgent and uses LangChain's AgentExecutor with tools for market data, portfolio management, and -simulated trading. +on-chain trading via Tenderly Virtual TestNet. """ import json @@ -21,7 +21,7 @@ from .base import BaseAgent from .data_classes import Trade, Portfolio, MarketData from .tools.market_data_tool import MarketDataTool -from .tools.make_trade_tool import MakeTradeTool +from .tools.trade_tool import TradeTool from .tools.database_tool import DatabaseTool from .tools.plan_tool import PlanTool from .memory import AgentMemory @@ -36,11 +36,19 @@ class TradingAgent(BaseAgent): This agent can: - Fetch and analyze market data - Manage a portfolio with cash and crypto holdings - - Execute simulated trades via MakeTradeTool + - Execute on-chain trades via TradeTool (Uniswap V3 on Tenderly Virtual TestNet) - Make decisions based on market conditions and personality - Persist all state to database (no local memory) """ + SUPPORTED_TOKENS = ["WETH", "CBBTC"] + + TOKEN_DECIMALS = { + "USDC": 6, + "WETH": 18, + "CBBTC": 8, + } + def __init__( self, agent_id: str, @@ -89,14 +97,11 @@ def __init__( # Initialize market data tool self.market_tool = MarketDataTool() - # Initialize simulated trade tool - self.make_trade_tool = MakeTradeTool( + # Initialize on-chain trade tool (Tenderly Virtual TestNet) + self.trade_tool = TradeTool( agent_id=agent_id, - portfolio=self.portfolio, - market_tool=self.market_tool, - database_tool=database_tool, agent_uuid=agent_uuid, - tournament_uuid=tournament_uuid, + database_tool=database_tool, ) # Initialize database-backed memory @@ -115,6 +120,9 @@ def __init__( database_tool=database_tool, ) + # Track cost basis for realized PnL (FIFO) + self._cost_basis: dict[str, list[dict]] = {} + # Track if we need to recover state if recover_from_crash and database_tool and agent_uuid and tournament_uuid: logger.info(f"Attempting crash recovery for agent={agent_id}") @@ -148,16 +156,16 @@ def _build_agent_executor(self) -> AgentExecutor: ), Tool( name="get_portfolio_status", - func=lambda _: self.make_trade_tool.get_portfolio_status(), + func=lambda _: self._get_portfolio_status(), description="Get current portfolio status including cash, holdings, and performance. Input: empty string", ), Tool( name="execute_trade", func=self._execute_trade_wrapper, description=( - "Execute a simulated trade. Format: 'ACTION TOKEN AMOUNT CONFIDENCE SUMMARY' " - "Example: 'BUY ETH 50 0.8 Bullish momentum detected'. " - "ACTION must be BUY or SELL. TOKEN must be ETH, BTC, SOL, AVAX, DOGE, XRP, TRX, SUI, LINK" + "Execute an on-chain trade via Uniswap V3. Format: 'ACTION TOKEN AMOUNT CONFIDENCE SUMMARY' " + "Example: 'BUY WETH 50 0.8 Bullish momentum detected'. " + "ACTION must be BUY or SELL. TOKEN must be WETH or CBBTC. " "AMOUNT is USDC for BUY, token quantity for SELL. " "CONFIDENCE is 0.0-1.0. SUMMARY is brief explanation." ), @@ -227,14 +235,14 @@ def _build_agent_executor(self) -> AgentExecutor: 5. After updating your plans, decide whether to execute any trades NOW based on current conditions. Guidelines: -- For BUY trades: amount is USDC to spend (e.g., BUY ETH 50 means spend $50 USDC to buy ETH) +- For BUY trades: amount is USDC to spend (e.g., BUY WETH 50 means spend $50 USDC to buy WETH) - For SELL trades: amount is quantity of token to sell -- Only trade with these tokens: (use token symbols: ETH, BTC, SOL, AVAX, DOGE, XRP, TRX, SUI, LINK) +- Only trade with these tokens: WETH, CBBTC - Check portfolio before trading - Conservative agents should trade less frequently - Aggressive agents can take larger positions - Always provide reasoning in your summary -- This is a simulation - trades are not executed on-chain +- Trades are executed on-chain via Uniswap V3 on Tenderly Virtual TestNet - Do not create duplicate plans — cancel outdated ones first TOOLS: @@ -285,7 +293,7 @@ def _build_agent_executor(self) -> AgentExecutor: def _execute_trade_wrapper(self, trade_input: str) -> str: """ - Wrapper for executing trades from LangChain tool. + Wrapper for executing on-chain trades from LangChain tool. Args: trade_input: String format "ACTION TOKEN AMOUNT CONFIDENCE SUMMARY" @@ -305,23 +313,50 @@ def _execute_trade_wrapper(self, trade_input: str) -> str: confidence = float(parts[3]) summary = parts[4] - # Execute trade - handle async properly + # Validate before executing on-chain + is_valid, reason = self._validate_trade(action, token, amount) + if not is_valid: + return f"Trade validation failed: {reason}" + + # Get current price for portfolio tracking + price = self.market_tool.get_price(token) + if not price or price <= 0: + return f"Error: Could not fetch price for {token}" + + # Execute on-chain trade via TradeTool trade = self._run_async( - self.make_trade_tool.execute_trade( + self.trade_tool.execute_trade( action=action, token=token, - amount=amount, + qty=amount, + price=price, confidence=confidence, summary=summary, - risk_score=self.risk_score, ) ) - logger.info(f"Trade executed successfully: {trade}") + # Update local portfolio state from the on-chain result + self._update_portfolio_from_trade(trade) + + # Save trade to database + if self.database_tool and self.agent_uuid and self.tournament_uuid: + try: + self._run_async( + self.database_tool.save_trade( + trade=trade, + agent_uuid=self.agent_uuid, + tournament_uuid=self.tournament_uuid, + ) + ) + except Exception as e: + logger.error(f"Failed to save trade to database: {e}") + + logger.info(f"Trade executed on-chain: {trade}") return ( - f"Trade executed successfully! " + f"Trade executed on-chain! " f"{action} {trade.qty:.6f} {token} at ${trade.price:.2f}. " + f"tx_hash: {trade.tx_hash}. " f"New cash: ${self.portfolio.cash:.2f}, " f"Total value: ${self.portfolio.total_value:.2f}" ) @@ -421,24 +456,146 @@ def _run_async(self, coro): # No loop - use asyncio.run() return asyncio.run(coro) - def validate_trade( - self, action: str, token: str, amount: float, price: float + # ============================================================================ + # PORTFOLIO MANAGEMENT + # ============================================================================ + + def _validate_trade( + self, action: str, token: str, amount: float, ) -> Tuple[bool, str]: - """ - Validate a proposed trade before execution. + """Validate a proposed trade against portfolio constraints.""" + action = action.upper() + token = token.upper() + + if action not in ["BUY", "SELL"]: + return False, f"Invalid action: {action}. Must be BUY or SELL" + + if token not in self.SUPPORTED_TOKENS: + return False, f"Unsupported token: {token}. Must be one of {self.SUPPORTED_TOKENS}" + + if amount <= 0: + return False, f"Invalid amount: {amount}. Must be positive" + + if action == "BUY": + if amount > self.portfolio.cash: + return False, f"Insufficient cash: have ${self.portfolio.cash:.2f}, need ${amount:.2f}" + max_trade_size = self.portfolio.total_value * self.risk_score + if amount > max_trade_size: + return False, f"Trade size ${amount:.2f} exceeds risk limit ${max_trade_size:.2f}" + elif action == "SELL": + current_holdings = self.portfolio.holdings.get(token, 0.0) + if amount > current_holdings: + return False, f"Insufficient {token}: have {current_holdings:.6f}, trying to sell {amount:.6f}" + + return True, "Trade validated" + + def _update_portfolio_from_trade(self, trade: Trade) -> None: + """Update local portfolio state after an on-chain trade.""" + token = trade.token + action = trade.action + + if action == "BUY": + usdc_spent = trade.qty * trade.price + self.portfolio.cash -= usdc_spent + self.portfolio.holdings[token] = ( + self.portfolio.holdings.get(token, 0.0) + trade.qty + ) + # Track cost basis + if token not in self._cost_basis: + self._cost_basis[token] = [] + self._cost_basis[token].append({"qty": trade.qty, "price": trade.price}) + + elif action == "SELL": + usdc_received = trade.qty * trade.price + self.portfolio.cash += usdc_received + current = self.portfolio.holdings.get(token, 0.0) + new_holdings = current - trade.qty + + # Calculate realized PnL via FIFO + realized_pnl = self._calculate_realized_pnl(token, trade.qty, trade.price) + if realized_pnl is not None: + self.portfolio.realized_pnl += realized_pnl + if realized_pnl > 0: + self.portfolio.num_winning_trades += 1 + elif realized_pnl < 0: + self.portfolio.num_losing_trades += 1 + + if new_holdings > 0.0001: + self.portfolio.holdings[token] = new_holdings + else: + self.portfolio.holdings.pop(token, None) + self._cost_basis.pop(token, None) + + self.portfolio.num_trades += 1 + self._recalculate_portfolio_metrics() + + def _calculate_realized_pnl(self, token: str, sell_qty: float, sell_price: float) -> float: + """Calculate realized PnL using FIFO.""" + if token not in self._cost_basis or not self._cost_basis[token]: + return 0.0 + + remaining_qty = sell_qty + total_cost = 0.0 + + while remaining_qty > 0 and self._cost_basis[token]: + lot = self._cost_basis[token][0] + if lot["qty"] <= remaining_qty: + total_cost += lot["qty"] * lot["price"] + remaining_qty -= lot["qty"] + self._cost_basis[token].pop(0) + else: + total_cost += remaining_qty * lot["price"] + lot["qty"] -= remaining_qty + remaining_qty = 0 + + return (sell_qty * sell_price) - total_cost + + def _recalculate_portfolio_metrics(self) -> None: + """Recalculate all portfolio metrics from current state.""" + pf = self.portfolio + + if pf.holdings: + holdings_val = 0.0 + for symbol, qty in pf.holdings.items(): + price = self.market_tool.get_price(symbol) + if price: + holdings_val += qty * price + pf.holdings_val = holdings_val + + pf.total_value = pf.holdings_val + pf.cash + + if pf.starting_val > 0: + pf.roi = (pf.total_value - pf.starting_val) / pf.starting_val + else: + pf.roi = 0.0 - Args: - action: BUY or SELL - token: Token symbol - amount: Trade amount - price: Current price + if pf.num_trades > 0: + pf.win_rate = pf.num_winning_trades / pf.num_trades + else: + pf.win_rate = 0.0 - Returns: - (is_valid, reason) - """ - return self.make_trade_tool.validate_trade( - action, token, amount, self.risk_score - ) + pf.unrealized_pnl = (pf.total_value - pf.starting_val) - pf.realized_pnl + + def _get_portfolio_status(self) -> dict: + """Get current portfolio status as a dictionary.""" + self._recalculate_portfolio_metrics() + return { + "cash": self.portfolio.cash, + "holdings": self.portfolio.holdings.copy(), + "holdings_val": self.portfolio.holdings_val, + "total_value": self.portfolio.total_value, + "roi": self.portfolio.roi, + "realized_pnl": self.portfolio.realized_pnl, + "unrealized_pnl": self.portfolio.unrealized_pnl, + "num_trades": self.portfolio.num_trades, + "win_rate": self.portfolio.win_rate, + } + + def validate_trade( + self, action: str, token: str, amount: float, price: float + ) -> Tuple[bool, str]: + """Validate a proposed trade before execution.""" + return self._validate_trade(action, token, amount) # Implement BaseAgent abstract methods @@ -453,7 +610,7 @@ def get_market_analysis(self) -> Dict[str, Any]: def get_portfolio_status(self) -> Dict[str, Any]: """Get current portfolio snapshot.""" - return self.make_trade_tool.get_portfolio_status() + return self._get_portfolio_status() def get_tournament_info(self) -> Dict[str, Any]: """Get tournament context.""" @@ -468,8 +625,8 @@ def get_long_term_memory(self, limit: int = 100) -> List[Trade]: return self._run_async(self.agent_memory.get_trade_history(limit)) def update_memory(self, trade: Trade) -> None: - """Save trade to database (no-op since MakeTradeTool handles this).""" - pass # MakeTradeTool saves trades directly to database + """Save trade to database (no-op since trades are saved in _execute_trade_wrapper).""" + pass def reset_tournament_memory(self) -> None: """Reset for new tournament (no-op for database-backed memory).""" @@ -525,15 +682,17 @@ async def execute_trade( confidence: float, summary: str, ) -> Trade: - """Execute a simulated trade and update portfolio.""" - return await self.make_trade_tool.execute_trade( + """Execute an on-chain trade and update portfolio.""" + trade = await self.trade_tool.execute_trade( action=action, token=token, - amount=qty, + qty=qty, + price=price, confidence=confidence, summary=summary, - risk_score=self.risk_score, ) + self._update_portfolio_from_trade(trade) + return trade def get_personality_response(self, trade: Trade) -> str: """Generate personality-driven response for a trade.""" @@ -564,7 +723,7 @@ def make_decision(self, task: Optional[str] = None) -> Dict[str, Any]: ) # Update portfolio values before making decision - self.make_trade_tool.recalculate_holdings_value() + self._recalculate_portfolio_metrics() # Get market context sentiment = self.market_tool.get_market_sentiment() @@ -605,7 +764,7 @@ def make_decision(self, task: Optional[str] = None) -> Dict[str, Any]: def evaluate_performance(self) -> Dict[str, Any]: """Compute performance metrics.""" # Update portfolio values - self.make_trade_tool.recalculate_holdings_value() + self._recalculate_portfolio_metrics() portfolio = self.portfolio return { @@ -671,14 +830,11 @@ async def recover_state(self) -> bool: self.portfolio.num_losing_trades = portfolio_data["num_losing_trades"] self.portfolio.win_rate = portfolio_data["win_rate"] - # Recreate make_trade_tool with restored portfolio - self.make_trade_tool = MakeTradeTool( + # Recreate trade_tool with restored state + self.trade_tool = TradeTool( agent_id=self.agent_id, - portfolio=self.portfolio, - market_tool=self.market_tool, - database_tool=self.database_tool, agent_uuid=self.agent_uuid, - tournament_uuid=self.tournament_uuid, + database_tool=self.database_tool, ) logger.info( diff --git a/backend/app/agents/onchain/ts_swap_wrapper.py b/backend/app/agents/onchain/ts_swap_wrapper.py index 4cafdec..8df9b6e 100644 --- a/backend/app/agents/onchain/ts_swap_wrapper.py +++ b/backend/app/agents/onchain/ts_swap_wrapper.py @@ -12,17 +12,17 @@ def execute_ts_swap( - agent_id: str, + private_key: str, from_token: Literal["USDC", "WETH", "CBBTC"], to_token: Literal["USDC", "WETH", "CBBTC"], amount: float, - slippage: int = 50 + slippage: int = 50, ) -> Dict[str, any]: """ Execute a swap using the TypeScript implementation. Args: - agent_id: Agent identifier (e.g., "agent_1") + private_key: The agent's private key for signing transactions from_token: Source token symbol to_token: Destination token symbol amount: Amount to swap (in token units, not wei) @@ -36,12 +36,10 @@ def execute_ts_swap( Raises: ValueError: If swap fails """ - # Get agent private key from env - private_key_var = f"{agent_id.upper()}_PRIVATE_KEY" - agent_private_key = os.getenv(private_key_var) + if not private_key: + raise ValueError("private_key is required") - if not agent_private_key: - raise ValueError(f"Missing environment variable: {private_key_var}") + agent_private_key = private_key # Get RPC URL (prioritize RPC_URL for testnet/mainnet, fallback to RPC_LOCAL for local dev) rpc_url = os.getenv("RPC_URL") or os.getenv("RPC_LOCAL", "http://127.0.0.1:8545") diff --git a/backend/app/agents/tools/__init__.py b/backend/app/agents/tools/__init__.py index 331a030..43b8918 100644 --- a/backend/app/agents/tools/__init__.py +++ b/backend/app/agents/tools/__init__.py @@ -13,23 +13,22 @@ ) from .market_data_tool import MarketDataTool -from .make_trade_tool import MakeTradeTool +from .trade_tool import TradeTool from .tweet_post_tool import TweetPostTool from .database_tool import DatabaseTool from .plan_tool import PlanTool # Deprecated: These tools are kept for backwards compatibility -# Use MakeTradeTool instead for simulated trading -from .trade_tool import TradeTool # Deprecated: on-chain execution -from .portfolio_tool import PortfolioTool # Deprecated: use MakeTradeTool +from .make_trade_tool import MakeTradeTool # Deprecated: use TradeTool for on-chain execution +from .portfolio_tool import PortfolioTool # Deprecated: use TradeTool __all__ = [ "MarketDataTool", - "MakeTradeTool", + "TradeTool", "TweetPostTool", "DatabaseTool", "PlanTool", # Deprecated - "TradeTool", + "MakeTradeTool", "PortfolioTool", ] diff --git a/backend/app/agents/tools/trade_tool.py b/backend/app/agents/tools/trade_tool.py index b3a4c46..4bd2dd8 100644 --- a/backend/app/agents/tools/trade_tool.py +++ b/backend/app/agents/tools/trade_tool.py @@ -185,12 +185,11 @@ async def execute_trade( if action == "BUY": logger.debug(f"Swapping {qty} USDC for {token}") swap_result = execute_ts_swap( - agent_id=self.agent_id, + private_key=private_key, from_token="USDC", to_token=token, amount=qty, slippage=50, - private_key=private_key, # Pass decrypted key directly ) amount_out_wei = int(swap_result["amount_out"]) actual_qty = amount_out_wei / (10 ** token_decimals[token]) @@ -201,12 +200,11 @@ async def execute_trade( else: logger.debug(f"Swapping {qty} {token} for USDC") swap_result = execute_ts_swap( - agent_id=self.agent_id, + private_key=private_key, from_token=token, to_token="USDC", amount=qty, slippage=50, - private_key=private_key, # Pass decrypted key directly ) amount_out_wei = int(swap_result["amount_out"]) usdc_received = amount_out_wei / (10 ** token_decimals["USDC"]) diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule index e48f39d25c493268da98f00f0852e0294e22acb2..2906ca7e9761a187d67962b23ae5f500012ad8d3 100644 GIT binary patch literal 16384 zcmeI3O=}ZD7{@nlG^wpvjW2~lRVi^q^M{egZ+C$xNChZV$FtwLF2{ncd7Y&;R*l=FcXO53lYQ zjDEvt9?;0PN4KwKdsUtc(by^w z$N@P>0=N!v9pE~^eSkiIK7c-eK7c-eK7c-eKCs7ppj+)~qffWSMb(+gs&-ai59ieN zr^~H@zu(F!f50m(67MT2$N@Pd_Xn+Fh;dsb-^@8;P}^g%Fo=R(Q39KEcyH!MqVu-WHr0d{1*Ty=KChB32RJPYz6M> zp8*WseedqHu%so^z^?eTx|)4qZO*n~>fa9D{`hBS(O775<#P@jg& zR)yM3@Pl_8msVq+UI#(TVqFy73@_UCx@FJ7tdSMz_d`q1MqoCwIa|JJGs?ULDtNoQ zE~XFUzINpbW0@}NGrUuYJ)e02WY5h}W-}H-CZ1oZ1=I_k)S&wSx-+s1_j^bj)IDe3 zr{bBoEmXR^vn#TMvH@c<=Y>1y4l;;nh{{L<&a%%B(Rc^NEp5bRxtJb3lj3#-F?8?Hgq zm9p_16s*n4b6d~}AneSWtQLoXy#Ub72oS|e);PbEDytbo+A)NMP>IK26+vK9VX3Wf z#2wvQ#_^CkHCTwI6WS512~#bACY)=0G*yKTAv%P#LX!C>JM%B4V>lGo>2yuzj(?{?Brxu>1k_6WnAT@JFt^Sf7edDKW8WE$qmm)00|%gB!C2vKqrBp?I0n5 delta 664 zcmZo@U~EXR2@dqhEJ;;J%P-1JEKx9K5MW?pVsKDUU|?Wi0%B$$h5<$(g9XL`(u@-g zl?9m?^vb$<`F}7l@nGr0tWnVf@M9Yb6d zLL8lZTophf3L0DrK#-kUso><}@1zjw=NTC4s^I4zqTmP&3n)}o9InRzLx6~%=)KySn+mX_oL`D&>Z@gU>l4M8Nx4;&zO^8zsocPRt^9sa}o z>$yue8m4j!b(nLrNKHsd%_}L&Of7C;nW{KB(x8ijQBc%MnLW;G@@s>BC7z7b#GH~0 z-Q=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", @@ -894,127 +951,523 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz", - "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==", - "license": "BSD-3-Clause" + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@lit/reactive-element": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz", - "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==", - "license": "BSD-3-Clause", - "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.4.0" + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@metamask/eth-json-rpc-provider": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-1.0.1.tgz", - "integrity": "sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==", - "dependencies": { - "@metamask/json-rpc-engine": "^7.0.0", - "@metamask/safe-event-emitter": "^3.0.0", - "@metamask/utils": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/json-rpc-engine": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz", - "integrity": "sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==", - "license": "ISC", - "dependencies": { - "@metamask/rpc-errors": "^6.2.1", - "@metamask/safe-event-emitter": "^3.0.0", - "@metamask/utils": "^8.3.0" - }, - "engines": { - "node": ">=16.0.0" + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/json-rpc-engine/node_modules/@metamask/utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", - "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", - "license": "ISC", - "dependencies": { - "@ethereumjs/tx": "^4.2.0", - "@metamask/superstruct": "^3.0.0", - "@noble/hashes": "^1.3.1", - "@scure/base": "^1.1.3", - "@types/debug": "^4.1.7", - "debug": "^4.3.4", - "pony-cause": "^2.1.10", - "semver": "^7.5.4", - "uuid": "^9.0.1" - }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16.0.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" } }, - "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/rpc-errors": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", - "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", - "license": "MIT", - "dependencies": { - "@metamask/utils": "^9.0.0", - "fast-safe-stringify": "^2.0.6" - }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz", + "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz", + "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.4.0" + } + }, + "node_modules/@metamask/eth-json-rpc-provider": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-1.0.1.tgz", + "integrity": "sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==", + "dependencies": { + "@metamask/json-rpc-engine": "^7.0.0", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/json-rpc-engine": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz", + "integrity": "sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==", + "license": "ISC", + "dependencies": { + "@metamask/rpc-errors": "^6.2.1", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/json-rpc-engine/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, "engines": { "node": ">=16.0.0" } @@ -1547,38 +2000,156 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.0.tgz", + "integrity": "sha512-s5j2iFGp38QsG1LWRQaE2iUY3h1jc014/melHFfLdrsMJPqxqDQwWNwyQTcNoUSGZlCVZuM7t7JDMmSyRilsnA==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.0.tgz", + "integrity": "sha512-IB7RzmmtrPOrpAgEBR1PIQPD0yea5lggh5cq54m51jHjjljU80Ia+czfxJYMlSDl1DPvpzb8S9TalCc0VMo9Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.0.tgz", + "integrity": "sha512-/CntqDCnk5w2qIwMiF0a9r6+9qunZzFmU0cBX4T82LOflE72zzH6gnOjCwUXYKOBlQi8OpP/rMj8cBIr18x4TA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.0.tgz", + "integrity": "sha512-hB4GZnJGKa8m4efvTGNyii6qs76vTNl+3dKHTCAUaksN6KjYy4iEO3Q5ira405NW2PKb3EcqWiRaL9DrYJfMHg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.0.tgz", + "integrity": "sha512-E2IHMdE+C1k+nUgndM13/BY/iJY9KGCphCftMh7SXWcaQqExq/pJU/1Hgn8n/tFwSoLoYC/yUghOv97tAsIxqg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.0.tgz", + "integrity": "sha512-xzgl7c7BVk4+7PDWldU+On2nlwnGgFqJ1siWp3/8S0KBBLCjonB6zwJYPtl4MUY7YZJrzzumdUpUoquu5zk8vg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@next/env": { + "node_modules/@next/swc-linux-x64-gnu": { "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.0.tgz", - "integrity": "sha512-s5j2iFGp38QsG1LWRQaE2iUY3h1jc014/melHFfLdrsMJPqxqDQwWNwyQTcNoUSGZlCVZuM7t7JDMmSyRilsnA==", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.0.tgz", + "integrity": "sha512-sdyOg4cbiCw7YUr0F/7ya42oiVBXLD21EYkSwN+PhE4csJH4MSXUsYyslliiiBwkM+KsuQH/y9wuxVz6s7Nstg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@next/eslint-plugin-next": { + "node_modules/@next/swc-linux-x64-musl": { "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.0.tgz", - "integrity": "sha512-IB7RzmmtrPOrpAgEBR1PIQPD0yea5lggh5cq54m51jHjjljU80Ia+czfxJYMlSDl1DPvpzb8S9TalCc0VMo9Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.0.tgz", + "integrity": "sha512-IAXv3OBYqVaNOgyd3kxR4L3msuhmSy1bcchPHxDOjypG33i2yDWvGBwFD94OuuTjjTt/7cuIKtAmoOOml6kfbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@next/swc-darwin-arm64": { + "node_modules/@next/swc-win32-arm64-msvc": { "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.0.tgz", - "integrity": "sha512-/CntqDCnk5w2qIwMiF0a9r6+9qunZzFmU0cBX4T82LOflE72zzH6gnOjCwUXYKOBlQi8OpP/rMj8cBIr18x4TA==", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.0.tgz", + "integrity": "sha512-bmo3ncIJKUS9PWK1JD9pEVv0yuvp1KPuOsyJTHXTv8KDrEmgV/K+U0C75rl9rhIaODcS7JEb6/7eJhdwXI0XmA==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ - "darwin" + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.0.tgz", + "integrity": "sha512-O1cJbT+lZp+cTjYyZGiDwsOjO3UHHzSqobkPNipdlnnuPb1swfcuY6r3p8dsKU4hAIEO4cO67ZCfVVH/M1ETXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" ], "engines": { "node": ">= 10" @@ -1589,6 +2160,7 @@ "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "license": "MIT", + "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -2046,6 +2618,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -2062,16 +2635,6 @@ } } }, - "node_modules/@reown/appkit-controllers/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@reown/appkit-pay": { "version": "1.7.8", "resolved": "https://registry.npmjs.org/@reown/appkit-pay/-/appkit-pay-1.7.8.tgz", @@ -2428,6 +2991,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -2444,16 +3008,6 @@ } } }, - "node_modules/@reown/appkit-utils/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@reown/appkit-wallet": { "version": "1.7.8", "resolved": "https://registry.npmjs.org/@reown/appkit-wallet/-/appkit-wallet-1.7.8.tgz", @@ -2762,6 +3316,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -2778,16 +3333,6 @@ } } }, - "node_modules/@reown/appkit/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -3149,6 +3694,7 @@ "resolved": "https://registry.npmjs.org/@solana/kit/-/kit-3.0.3.tgz", "integrity": "sha512-CEEhCDmkvztd1zbgADsEQhmj9GyWOOGeW1hZD+gtwbBSF5YN1uofS/pex5MIh/VIqKRj+A2UnYWI1V+9+q/lyQ==", "license": "MIT", + "peer": true, "dependencies": { "@solana/accounts": "3.0.3", "@solana/addresses": "3.0.3", @@ -3692,58 +4238,258 @@ "tslib": "^2.8.0" } }, - "node_modules/@tailwindcss/node": { + "node_modules/@tailwindcss/node": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", - "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", - "jiti": "^2.6.1", - "lightningcss": "1.30.2", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.17" + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide": { + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", - "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-x64": "4.1.17", - "@tailwindcss/oxide-freebsd-x64": "4.1.17", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-x64-musl": "4.1.17", - "@tailwindcss/oxide-wasm32-wasi": "4.1.17", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", - "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">= 10" @@ -3786,6 +4532,7 @@ "version": "5.90.11", "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.11.tgz", "integrity": "sha512-3uyzz01D1fkTLXuxF3JfoJoHQMU2fxsfJwE+6N5hHy0dVNoZOvwKP8Z2k7k1KDeD54N20apcJnG75TBAStIrBA==", + "peer": true, "dependencies": { "@tanstack/query-core": "5.90.11" }, @@ -3814,6 +4561,17 @@ "react": "^18 || ^19" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3880,6 +4638,7 @@ "integrity": "sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3961,6 +4720,7 @@ "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/types": "8.47.0", @@ -4174,52 +4934,279 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", - "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", + "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.47.0", + "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/typescript-estree": "8.47.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", + "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.47.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", - "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@typescript-eslint/types": "8.47.0", - "eslint-visitor-keys": "^4.2.1" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=14.0.0" } }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", "cpu": [ "arm64" ], @@ -4227,7 +5214,35 @@ "license": "MIT", "optional": true, "os": [ - "darwin" + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" ] }, "node_modules/@wagmi/connectors": { @@ -4265,6 +5280,7 @@ "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.22.1.tgz", "integrity": "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==", "license": "MIT", + "peer": true, "dependencies": { "eventemitter3": "5.0.1", "mipd": "0.0.7", @@ -4865,6 +5881,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -4881,16 +5898,6 @@ } } }, - "node_modules/@walletconnect/utils/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@walletconnect/window-getters": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", @@ -4949,6 +5956,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5280,6 +6288,7 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -5434,6 +6443,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -5487,6 +6497,7 @@ "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -5768,6 +6779,7 @@ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", "license": "MIT", + "peer": true, "dependencies": { "node-fetch": "^2.7.0" } @@ -6080,6 +7092,7 @@ "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.16.tgz", "integrity": "sha512-dS5cbA9rA2VR4Ybuvhg6jvdmp46ubLn3E+px8cG/35aEDNclrqoCjg6mt0HYZ/M+OoESS3jSkCrqk1kWAEhWAw==", "license": "MIT", + "peer": true, "dependencies": { "@ecies/ciphers": "^0.2.4", "@noble/ciphers": "^1.3.0", @@ -6437,6 +7450,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6622,6 +7636,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7031,7 +8046,8 @@ "version": "6.4.9", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/eventemitter3": { "version": "5.0.1", @@ -8479,101 +9495,311 @@ "readable-stream": "^3.6.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", + "license": "MIT" + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/keyvaluestorage-interface": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", - "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", - "license": "MIT" - }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "CC0-1.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss": { + "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" } }, - "node_modules/lightningcss-darwin-arm64": { + "node_modules/lightningcss-win32-x64-msvc": { "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ - "darwin" + "win32" ], "engines": { "node": ">= 12.0.0" @@ -9746,6 +10972,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9755,6 +10982,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -9783,6 +11011,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10316,6 +11545,7 @@ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", "license": "MIT", + "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -10781,6 +12011,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -10971,6 +12202,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11232,6 +12464,7 @@ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -11242,6 +12475,7 @@ "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -11282,6 +12516,7 @@ "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.13.2.tgz", "integrity": "sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==", "license": "MIT", + "peer": true, "dependencies": { "derive-valtio": "0.1.0", "proxy-compare": "2.6.0", @@ -11323,6 +12558,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", @@ -11347,6 +12583,7 @@ "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.19.4.tgz", "integrity": "sha512-jjGwxoISGlEkuxE80u+cHRr4IeZNjoVgVItUo8PRyT3gbL8FYIsGdb+qUuTAOOb08lr/NaySRjty5zJDaZcn8w==", "license": "MIT", + "peer": true, "dependencies": { "@wagmi/connectors": "6.1.4", "@wagmi/core": "2.22.1", @@ -11534,6 +12771,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -11685,6 +12923,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -11730,111 +12969,6 @@ "optional": true } } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.0.tgz", - "integrity": "sha512-hB4GZnJGKa8m4efvTGNyii6qs76vTNl+3dKHTCAUaksN6KjYy4iEO3Q5ira405NW2PKb3EcqWiRaL9DrYJfMHg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.0.tgz", - "integrity": "sha512-E2IHMdE+C1k+nUgndM13/BY/iJY9KGCphCftMh7SXWcaQqExq/pJU/1Hgn8n/tFwSoLoYC/yUghOv97tAsIxqg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.0.tgz", - "integrity": "sha512-xzgl7c7BVk4+7PDWldU+On2nlwnGgFqJ1siWp3/8S0KBBLCjonB6zwJYPtl4MUY7YZJrzzumdUpUoquu5zk8vg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.0.tgz", - "integrity": "sha512-sdyOg4cbiCw7YUr0F/7ya42oiVBXLD21EYkSwN+PhE4csJH4MSXUsYyslliiiBwkM+KsuQH/y9wuxVz6s7Nstg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.0.tgz", - "integrity": "sha512-IAXv3OBYqVaNOgyd3kxR4L3msuhmSy1bcchPHxDOjypG33i2yDWvGBwFD94OuuTjjTt/7cuIKtAmoOOml6kfbg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.0.tgz", - "integrity": "sha512-bmo3ncIJKUS9PWK1JD9pEVv0yuvp1KPuOsyJTHXTv8KDrEmgV/K+U0C75rl9rhIaODcS7JEb6/7eJhdwXI0XmA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.0.tgz", - "integrity": "sha512-O1cJbT+lZp+cTjYyZGiDwsOjO3UHHzSqobkPNipdlnnuPb1swfcuY6r3p8dsKU4hAIEO4cO67ZCfVVH/M1ETXA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } From a0035bebcdf658039eb46d5d5854f57ac1f6d5e2 Mon Sep 17 00:00:00 2001 From: Jacob Horne Date: Mon, 16 Feb 2026 13:51:21 -0800 Subject: [PATCH 3/5] Fix merge conflicts for trade tool integration pr --- backend/app/agents/executor.py | 69 +++------------------------------ backend/celerybeat-schedule | Bin 16384 -> 0 bytes 2 files changed, 5 insertions(+), 64 deletions(-) delete mode 100644 backend/celerybeat-schedule diff --git a/backend/app/agents/executor.py b/backend/app/agents/executor.py index 0c615de..c28074a 100644 --- a/backend/app/agents/executor.py +++ b/backend/app/agents/executor.py @@ -22,13 +22,10 @@ from .data_classes import Trade, Portfolio, MarketData from .tools.market_data_tool import MarketDataTool from .tools.trade_tool import TradeTool -#from .tools.make_trade_tool import MakeTradeTool from .tools.math_tool import MathTool from .tools.database_tool import DatabaseTool from .tools.research_tool import ResearchTool - from .tools.plan_tool import PlanTool -from .tools.research_tool import ResearchTool from .memory import AgentMemory logger = logging.getLogger(__name__) @@ -104,19 +101,15 @@ def __init__( # Initialize on-chain trade tool (Tenderly Virtual TestNet) self.trade_tool = TradeTool( - # Initialize math tool (technical indicators) - self.math_tool = MathTool(market_tool=self.market_tool) - # Intilialize research tool - self.research_tool = ResearchTool() - - ''' - # Initialize simulated trade tool - self.make_trade_tool = MakeTradeTool( agent_id=agent_id, agent_uuid=agent_uuid, database_tool=database_tool, ) - ''' + + # Initialize math tool (technical indicators) + self.math_tool = MathTool(market_tool=self.market_tool) + # Initialize research tool + self.research_tool = ResearchTool() # Initialize database-backed memory self.agent_memory = AgentMemory( @@ -235,14 +228,6 @@ def _build_agent_executor(self) -> AgentExecutor: "Example: 'abc123-uuid 2025-06-16T10:00:00Z'" ), ), - Tool( - name="research_token", - func=self._execute_research_token_wrapper, - description=( - "Research a token for news and any market sentiments, " - "Input: token symbol and recency (e.g., 'ETH 7d' for last 7 days)" - ), - ), ] # Create OpenAI LLM @@ -634,50 +619,6 @@ def _get_technical_indicator_wrapper(self, input_str: str) -> str: error_msg = f"Error computing indicator: {e}" logger.error(error_msg) return error_msg - def _execute_research_token_wrapper(self, input_str: str) -> str: - """ - Wrapper for executing research token from LangChain tool. - - Args: - input_str: "ETH" or "ETH 7d" - - Returns: - Research summary string - """ - from .tools.research_tool import research_result_to_dict - - try: - parts = input_str.split() - if len(parts) < 1: - return "Error: Invalid format. Expected 'TOKEN' or 'TOKEN RECENCY'" - - token = parts[0].upper() - recency = parts[1] if len(parts) > 1 else "7d" - - # Call sync research method - research_result = self.research_tool.research_token( - token_symbol=token, - recency=recency, - ) - - # Persist result to DB (convert dataclass to dict) - if self.database_tool and self.agent_uuid: - result_dict = research_result_to_dict(research_result) - self._run_async( - self.database_tool.save_research_result( - agent_uuid=self.agent_uuid, - query=f"Research {token}", - result=result_dict, - recency=recency, - related_tokens=[token], - ) - ) - - return research_result.summary_markdown - - except Exception as e: - logger.error(f"Research tool error: {e}") - return f"Research tool error: {str(e)}" def _create_plan_step_wrapper(self, input_str: str) -> str: """Parse: 'ACTION_TYPE EXECUTE_AT_ISO PAYLOAD_JSON'""" diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule deleted file mode 100644 index 2906ca7e9761a187d67962b23ae5f500012ad8d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3O=}ZD7{@nlG^wpvjW2~lRVi^q^M{egZ+C$xNChZV$FtwLF2{ncd7Y&;R*l=FcXO53lYQ zjDEvt9?;0PN4KwKdsUtc(by^w z$N@P>0=N!v9pE~^eSkiIK7c-eK7c-eK7c-eKCs7ppj+)~qffWSMb(+gs&-ai59ieN zr^~H@zu(F!f50m(67MT2$N@Pd_Xn+Fh;dsb-^@8;P}^g%Fo=R(Q39KEcyH!MqVu-WHr0d{1*Ty=KChB32RJPYz6M> zp8*WseedqHu%so^z^?eTx|)4qZO*n~>fa9D{`hBS(O775<#P@jg& zR)yM3@Pl_8msVq+UI#(TVqFy73@_UCx@FJ7tdSMz_d`q1MqoCwIa|JJGs?ULDtNoQ zE~XFUzINpbW0@}NGrUuYJ)e02WY5h}W-}H-CZ1oZ1=I_k)S&wSx-+s1_j^bj)IDe3 zr{bBoEmXR^vn#TMvH@c<=Y>1y4l;;nh{{L<&a%%B(Rc^NEp5bRxtJb3lj3#-F?8?Hgq zm9p_16s*n4b6d~}AneSWtQLoXy#Ub72oS|e);PbEDytbo+A)NMP>IK26+vK9VX3Wf z#2wvQ#_^CkHCTwI6WS512~#bACY)=0G*yKTAv%P#LX!C>JM%B4V>lGo>2yuzj(?{?Brxu>1k_6WnAT@JFt^Sf7edDKW8WE$qmm)00|%gB!C2vKqrBp?I0n5 From 15bdc771236bbf9498a39bc5bf4e7a0c7ae93f4e Mon Sep 17 00:00:00 2001 From: Jacob Horne Date: Mon, 16 Feb 2026 14:10:00 -0800 Subject: [PATCH 4/5] Fix make trade tool tests --- backend/tests/test_make_trade_tool.py | 98 +++++++++++++-------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/backend/tests/test_make_trade_tool.py b/backend/tests/test_make_trade_tool.py index 3f837b9..5baab8f 100644 --- a/backend/tests/test_make_trade_tool.py +++ b/backend/tests/test_make_trade_tool.py @@ -22,8 +22,6 @@ def mock_market_tool(): """Create a mock market data tool with fixed prices.""" market_tool = Mock() market_tool.get_price = Mock(side_effect=lambda token: { - "WETH": 3000.0, - "CBBTC": 60000.0, "ETH": 3000.0, "BTC": 60000.0, }.get(token.upper(), None)) @@ -48,7 +46,7 @@ def portfolio_with_holdings(): return Portfolio( agent_id="test_agent", cash=200.0, - holdings={"WETH": 0.1}, # 0.1 ETH worth ~$300 + holdings={"ETH": 0.1}, # 0.1 ETH worth ~$300 starting_val=500.0, holdings_val=300.0, total_value=500.0, @@ -90,57 +88,57 @@ class TestValidation: """Test trade validation logic.""" def test_valid_buy_trade(self, make_trade_tool): - is_valid, reason = make_trade_tool.validate_trade("BUY", "WETH", 100.0) + is_valid, reason = make_trade_tool.validate_trade("BUY", "ETH", 100.0) assert is_valid is True assert reason == "Trade validated" def test_valid_sell_trade(self, make_trade_tool_with_holdings): - is_valid, reason = make_trade_tool_with_holdings.validate_trade("SELL", "WETH", 0.05) + is_valid, reason = make_trade_tool_with_holdings.validate_trade("SELL", "ETH", 0.05) assert is_valid is True assert reason == "Trade validated" def test_invalid_action(self, make_trade_tool): - is_valid, reason = make_trade_tool.validate_trade("HOLD", "WETH", 100.0) + is_valid, reason = make_trade_tool.validate_trade("HOLD", "ETH", 100.0) assert is_valid is False assert "Invalid action" in reason def test_unsupported_token(self, make_trade_tool): - is_valid, reason = make_trade_tool.validate_trade("BUY", "DOGE", 100.0) + is_valid, reason = make_trade_tool.validate_trade("BUY", "SHIB", 100.0) assert is_valid is False assert "Unsupported token" in reason def test_insufficient_cash_for_buy(self, make_trade_tool): - is_valid, reason = make_trade_tool.validate_trade("BUY", "WETH", 600.0) + is_valid, reason = make_trade_tool.validate_trade("BUY", "ETH", 600.0) assert is_valid is False assert "Insufficient cash" in reason def test_insufficient_holdings_for_sell(self, make_trade_tool_with_holdings): - is_valid, reason = make_trade_tool_with_holdings.validate_trade("SELL", "WETH", 1.0) + is_valid, reason = make_trade_tool_with_holdings.validate_trade("SELL", "ETH", 1.0) assert is_valid is False - assert "Insufficient WETH" in reason + assert "Insufficient ETH" in reason def test_zero_amount(self, make_trade_tool): - is_valid, reason = make_trade_tool.validate_trade("BUY", "WETH", 0.0) + is_valid, reason = make_trade_tool.validate_trade("BUY", "ETH", 0.0) assert is_valid is False assert "Invalid amount" in reason def test_negative_amount(self, make_trade_tool): - is_valid, reason = make_trade_tool.validate_trade("BUY", "WETH", -100.0) + is_valid, reason = make_trade_tool.validate_trade("BUY", "ETH", -100.0) assert is_valid is False assert "Invalid amount" in reason def test_risk_limit_exceeded(self, make_trade_tool): # With risk_score=0.5, max trade is 50% of $500 = $250 - is_valid, reason = make_trade_tool.validate_trade("BUY", "WETH", 300.0, risk_score=0.5) + is_valid, reason = make_trade_tool.validate_trade("BUY", "ETH", 300.0, risk_score=0.5) assert is_valid is False assert "exceeds risk limit" in reason def test_case_insensitive_action(self, make_trade_tool): - is_valid, _ = make_trade_tool.validate_trade("buy", "WETH", 100.0) + is_valid, _ = make_trade_tool.validate_trade("buy", "ETH", 100.0) assert is_valid is True def test_case_insensitive_token(self, make_trade_tool): - is_valid, _ = make_trade_tool.validate_trade("BUY", "weth", 100.0) + is_valid, _ = make_trade_tool.validate_trade("BUY", "eth", 100.0) assert is_valid is True @@ -156,7 +154,7 @@ class TestBuyTrades: async def test_basic_buy(self, make_trade_tool): trade = await make_trade_tool.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=150.0, # $150 USDC confidence=0.8, summary="Test buy", @@ -164,7 +162,7 @@ async def test_basic_buy(self, make_trade_tool): # Check trade details assert trade.action == "BUY" - assert trade.token == "WETH" + assert trade.token == "ETH" assert trade.price == 3000.0 assert trade.qty == pytest.approx(0.05) # $150 / $3000 = 0.05 ETH assert trade.confidence == 0.8 @@ -173,43 +171,43 @@ async def test_basic_buy(self, make_trade_tool): # Check portfolio updates assert make_trade_tool.portfolio.cash == pytest.approx(350.0) # $500 - $150 - assert make_trade_tool.portfolio.holdings["WETH"] == pytest.approx(0.05) + assert make_trade_tool.portfolio.holdings["ETH"] == pytest.approx(0.05) assert make_trade_tool.portfolio.num_trades == 1 @pytest.mark.asyncio async def test_buy_adds_to_existing_holdings(self, make_trade_tool_with_holdings): await make_trade_tool_with_holdings.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=60.0, # $60 USDC = 0.02 ETH confidence=0.7, summary="Add to position", ) # Should add to existing 0.1 ETH - assert make_trade_tool_with_holdings.portfolio.holdings["WETH"] == pytest.approx(0.12) + assert make_trade_tool_with_holdings.portfolio.holdings["ETH"] == pytest.approx(0.12) assert make_trade_tool_with_holdings.portfolio.cash == pytest.approx(140.0) # $200 - $60 @pytest.mark.asyncio async def test_buy_different_token(self, make_trade_tool_with_holdings): await make_trade_tool_with_holdings.execute_trade( action="BUY", - token="CBBTC", + token="BTC", amount=60.0, # $60 USDC confidence=0.75, summary="Diversify into BTC", ) # Should have both tokens - assert "WETH" in make_trade_tool_with_holdings.portfolio.holdings - assert "CBBTC" in make_trade_tool_with_holdings.portfolio.holdings - assert make_trade_tool_with_holdings.portfolio.holdings["CBBTC"] == pytest.approx(0.001) # $60 / $60000 + assert "ETH" in make_trade_tool_with_holdings.portfolio.holdings + assert "BTC" in make_trade_tool_with_holdings.portfolio.holdings + assert make_trade_tool_with_holdings.portfolio.holdings["BTC"] == pytest.approx(0.001) # $60 / $60000 @pytest.mark.asyncio async def test_buy_updates_metrics(self, make_trade_tool): await make_trade_tool.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=100.0, confidence=0.8, summary="Test", @@ -218,7 +216,7 @@ async def test_buy_updates_metrics(self, make_trade_tool): status = make_trade_tool.get_portfolio_status() assert status["num_trades"] == 1 assert status["cash"] == pytest.approx(400.0) - assert "WETH" in status["holdings"] + assert "ETH" in status["holdings"] # ============================================================================ @@ -233,7 +231,7 @@ class TestSellTrades: async def test_basic_sell(self, make_trade_tool_with_holdings): trade = await make_trade_tool_with_holdings.execute_trade( action="SELL", - token="WETH", + token="ETH", amount=0.05, # Sell half (0.05 of 0.1 ETH) confidence=0.75, summary="Take profit", @@ -241,26 +239,26 @@ async def test_basic_sell(self, make_trade_tool_with_holdings): # Check trade details assert trade.action == "SELL" - assert trade.token == "WETH" + assert trade.token == "ETH" assert trade.qty == 0.05 assert trade.price == 3000.0 # Check portfolio updates - assert make_trade_tool_with_holdings.portfolio.holdings["WETH"] == pytest.approx(0.05) + assert make_trade_tool_with_holdings.portfolio.holdings["ETH"] == pytest.approx(0.05) assert make_trade_tool_with_holdings.portfolio.cash == pytest.approx(350.0) # $200 + $150 @pytest.mark.asyncio async def test_sell_all_removes_from_holdings(self, make_trade_tool_with_holdings): await make_trade_tool_with_holdings.execute_trade( action="SELL", - token="WETH", + token="ETH", amount=0.1, # Sell all confidence=0.9, summary="Exit position", ) # Holdings should be empty for WETH - assert "WETH" not in make_trade_tool_with_holdings.portfolio.holdings + assert "ETH" not in make_trade_tool_with_holdings.portfolio.holdings assert make_trade_tool_with_holdings.portfolio.cash == pytest.approx(500.0) # $200 + $300 @pytest.mark.asyncio @@ -268,7 +266,7 @@ async def test_sell_calculates_realized_pnl(self, make_trade_tool): # First buy at $3000 await make_trade_tool.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=300.0, # Buy 0.1 ETH at $3000 confidence=0.8, summary="Initial buy", @@ -280,7 +278,7 @@ async def test_sell_calculates_realized_pnl(self, make_trade_tool): # Sell at higher price trade = await make_trade_tool.execute_trade( action="SELL", - token="WETH", + token="ETH", amount=0.1, # Sell all confidence=0.9, summary="Take profit", @@ -296,7 +294,7 @@ async def test_sell_losing_trade(self, make_trade_tool): # First buy at $3000 await make_trade_tool.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=300.0, confidence=0.8, summary="Initial buy", @@ -308,7 +306,7 @@ async def test_sell_losing_trade(self, make_trade_tool): # Sell at lower price trade = await make_trade_tool.execute_trade( action="SELL", - token="WETH", + token="ETH", amount=0.1, confidence=0.6, summary="Cut losses", @@ -331,19 +329,19 @@ class TestFIFOPnL: @pytest.mark.asyncio async def test_fifo_multiple_buys(self, make_trade_tool): # Buy 1: 0.05 ETH at $3000 - await make_trade_tool.execute_trade("BUY", "WETH", 150.0, 0.8, "First buy") + await make_trade_tool.execute_trade("BUY", "ETH", 150.0, 0.8, "First buy") # Change price to $3200 make_trade_tool.market_tool.get_price = Mock(return_value=3200.0) # Buy 2: 0.05 ETH at $3200 - await make_trade_tool.execute_trade("BUY", "WETH", 160.0, 0.8, "Second buy") + await make_trade_tool.execute_trade("BUY", "ETH", 160.0, 0.8, "Second buy") # Change price to $3500 make_trade_tool.market_tool.get_price = Mock(return_value=3500.0) # Sell first lot (FIFO - should use $3000 cost basis) - trade = await make_trade_tool.execute_trade("SELL", "WETH", 0.05, 0.9, "Sell first lot") + trade = await make_trade_tool.execute_trade("SELL", "ETH", 0.05, 0.9, "Sell first lot") # PnL: (3500 - 3000) * 0.05 = $25 assert trade.realized_pnl == pytest.approx(25.0) @@ -351,19 +349,19 @@ async def test_fifo_multiple_buys(self, make_trade_tool): @pytest.mark.asyncio async def test_fifo_partial_lot(self, make_trade_tool): # Buy 0.1 ETH at $3000 - await make_trade_tool.execute_trade("BUY", "WETH", 300.0, 0.8, "Initial buy") + await make_trade_tool.execute_trade("BUY", "ETH", 300.0, 0.8, "Initial buy") # Change price to $3300 make_trade_tool.market_tool.get_price = Mock(return_value=3300.0) # Sell half - trade = await make_trade_tool.execute_trade("SELL", "WETH", 0.05, 0.85, "Partial sell") + trade = await make_trade_tool.execute_trade("SELL", "ETH", 0.05, 0.85, "Partial sell") # PnL: (3300 - 3000) * 0.05 = $15 assert trade.realized_pnl == pytest.approx(15.0) # Sell remaining half - trade2 = await make_trade_tool.execute_trade("SELL", "WETH", 0.05, 0.85, "Sell rest") + trade2 = await make_trade_tool.execute_trade("SELL", "ETH", 0.05, 0.85, "Sell rest") # PnL: (3300 - 3000) * 0.05 = $15 assert trade2.realized_pnl == pytest.approx(15.0) @@ -380,7 +378,7 @@ class TestPortfolioMetrics: @pytest.mark.asyncio async def test_roi_calculation(self, make_trade_tool): # Buy some ETH - await make_trade_tool.execute_trade("BUY", "WETH", 250.0, 0.8, "Buy") + await make_trade_tool.execute_trade("BUY", "ETH", 250.0, 0.8, "Buy") # Price goes up 10% make_trade_tool.market_tool.get_price = Mock(return_value=3300.0) @@ -394,13 +392,13 @@ async def test_roi_calculation(self, make_trade_tool): @pytest.mark.asyncio async def test_win_rate_calculation(self, make_trade_tool): # Execute trades - await make_trade_tool.execute_trade("BUY", "WETH", 150.0, 0.8, "Buy 1") + await make_trade_tool.execute_trade("BUY", "ETH", 150.0, 0.8, "Buy 1") make_trade_tool.market_tool.get_price = Mock(return_value=3500.0) - await make_trade_tool.execute_trade("SELL", "WETH", 0.025, 0.9, "Win") # Winner + await make_trade_tool.execute_trade("SELL", "ETH", 0.025, 0.9, "Win") # Winner make_trade_tool.market_tool.get_price = Mock(return_value=2800.0) - await make_trade_tool.execute_trade("SELL", "WETH", 0.025, 0.6, "Loss") # Loser + await make_trade_tool.execute_trade("SELL", "ETH", 0.025, 0.6, "Loss") # Loser # 1 win, 1 loss out of 3 trades (1 buy + 2 sells) # But win_rate is based on winning vs losing trades only @@ -419,7 +417,7 @@ def test_get_portfolio_status(self, make_trade_tool_with_holdings): assert "win_rate" in status assert status["cash"] == 200.0 - assert status["holdings"]["WETH"] == 0.1 + assert status["holdings"]["ETH"] == 0.1 # ============================================================================ @@ -435,7 +433,7 @@ async def test_validation_error_raises_exception(self, make_trade_tool): with pytest.raises(MakeTradeToolError) as exc_info: await make_trade_tool.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=1000.0, # More than available confidence=0.8, summary="Should fail", @@ -449,7 +447,7 @@ async def test_price_fetch_error(self, make_trade_tool): with pytest.raises(MakeTradeToolError) as exc_info: await make_trade_tool.execute_trade( action="BUY", - token="WETH", + token="ETH", amount=100.0, confidence=0.8, summary="Should fail", @@ -480,7 +478,7 @@ async def test_trade_saved_to_database(self, base_portfolio, mock_market_tool): tournament_uuid=uuid4(), ) - await tool.execute_trade("BUY", "WETH", 100.0, 0.8, "Test") + await tool.execute_trade("BUY", "ETH", 100.0, 0.8, "Test") # Verify database was called mock_db.save_trade.assert_called_once() @@ -501,6 +499,6 @@ async def test_database_error_doesnt_fail_trade(self, base_portfolio, mock_marke ) # Trade should still succeed even if DB fails - trade = await tool.execute_trade("BUY", "WETH", 100.0, 0.8, "Test") + trade = await tool.execute_trade("BUY", "ETH", 100.0, 0.8, "Test") assert trade is not None assert tool.portfolio.cash == pytest.approx(400.0) From 828f6395ab58bf371fe4ae61feef13b966fe16ad Mon Sep 17 00:00:00 2001 From: rounak-Rao Date: Wed, 18 Feb 2026 18:22:42 -0800 Subject: [PATCH 5/5] added patch endpoint to update an existing agent's stats table with a funded wallet --- backend/app/api/routers/agent.py | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/backend/app/api/routers/agent.py b/backend/app/api/routers/agent.py index b894602..be2aa31 100644 --- a/backend/app/api/routers/agent.py +++ b/backend/app/api/routers/agent.py @@ -103,6 +103,48 @@ async def create_agent( return agent +@router.patch("/{agent_id}/wallet", response_model=AgentResponse) +async def provision_agent_wallet( + agent_id: UUID, + session: AsyncSession = Depends(get_db), + admin: dict = Depends(require_admin), +): + """ + PATCH route to generate and fund a wallet for an existing agent (admin only). + + Generates a new Ethereum wallet, stores credentials in agent stats, + and funds it via Tenderly (10,000 USDC + 1 ETH for gas). + """ + agent = await session.get(Agent, agent_id) + if not agent: + raise HTTPException(status_code=404, detail="Agent Not Found") + + # Generate and encrypt wallet + wallet = generate_wallet() + encrypted_key = encrypt_private_key(wallet["private_key"]) + + # Update stats with wallet credentials + agent.stats = { + **(agent.stats or {}), + "wallet_address": wallet["address"], + "encrypted_private_key": encrypted_key, + } + + session.add(agent) + await session.commit() + await session.refresh(agent) + logger.info(f"Wallet credentials stored for agent {agent_id}: {wallet['address']}") + + # Fund via Tenderly + try: + setup_agent_wallet(wallet_address=wallet["address"], initial_usdc=10000.0, initial_eth=1.0) + logger.info(f"Wallet funded successfully: {wallet['address']}") + except Exception as e: + logger.error(f"Failed to fund wallet for agent {agent_id}: {e}") + + return agent + + @router.put("/{agent_id}", response_model=AgentResponse) async def update_agent( agent_id: UUID,