فهرست منبع

Merge branch 'fix/skeleton' into feat/table-grid

Alexandru Cambose 1 ماه پیش
والد
کامیت
b5574f61ea
51فایلهای تغییر یافته به همراه1814 افزوده شده و 700 حذف شده
  1. 13 13
      Cargo.lock
  2. 1 1
      apps/hip-3-pusher/Dockerfile
  3. 9 0
      apps/hip-3-pusher/README.md
  4. 4 1
      apps/hip-3-pusher/config/config.toml
  5. 28 12
      apps/hip-3-pusher/pyproject.toml
  6. 0 35
      apps/hip-3-pusher/src/hyperliquid_listener.py
  7. 0 93
      apps/hip-3-pusher/src/kms_signer.py
  8. 0 73
      apps/hip-3-pusher/src/price_state.py
  9. 47 0
      apps/hip-3-pusher/src/pusher/config.py
  10. 2 0
      apps/hip-3-pusher/src/pusher/exception.py
  11. 25 16
      apps/hip-3-pusher/src/pusher/hermes_listener.py
  12. 85 0
      apps/hip-3-pusher/src/pusher/hyperliquid_listener.py
  13. 121 0
      apps/hip-3-pusher/src/pusher/kms_signer.py
  14. 27 18
      apps/hip-3-pusher/src/pusher/lazer_listener.py
  15. 13 11
      apps/hip-3-pusher/src/pusher/main.py
  16. 4 2
      apps/hip-3-pusher/src/pusher/metrics.py
  17. 83 0
      apps/hip-3-pusher/src/pusher/price_state.py
  18. 15 16
      apps/hip-3-pusher/src/pusher/publisher.py
  19. 70 0
      apps/hip-3-pusher/tests/test_price_state.py
  20. 138 108
      apps/hip-3-pusher/uv.lock
  21. 7 5
      apps/insights/src/services/pyth/get-feeds.ts
  22. 5 1
      contract_manager/scripts/deploy_evm_executor_contracts.ts
  23. 14 0
      contract_manager/store/chains/EvmChains.json
  24. 6 1
      contract_manager/store/contracts/EvmExecutorContracts.json
  25. 5 0
      contract_manager/store/contracts/EvmLazerContracts.json
  26. 5 0
      contract_manager/store/contracts/EvmPriceFeedContracts.json
  27. 11 1
      contract_manager/store/contracts/EvmWormholeContracts.json
  28. 2 0
      governance/xc_admin/packages/xc_admin_common/src/chains.ts
  29. 2 2
      lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml
  30. 6 27
      lazer/contracts/sui/sdk/js/examples/fetch-and-verify-update.ts
  31. 1 1
      lazer/contracts/sui/sdk/js/package.json
  32. 2 2
      lazer/publisher_sdk/rust/Cargo.toml
  33. 48 0
      lazer/sdk/js/examples/history.ts
  34. 0 89
      lazer/sdk/js/examples/index.ts
  35. 125 0
      lazer/sdk/js/examples/streaming.ts
  36. 29 0
      lazer/sdk/js/examples/symbols.ts
  37. 131 0
      lazer/sdk/js/examples/util.ts
  38. 5 2
      lazer/sdk/js/package.json
  39. 200 15
      lazer/sdk/js/src/client.ts
  40. 7 0
      lazer/sdk/js/src/constants.ts
  41. 61 0
      lazer/sdk/js/src/protocol.ts
  42. 19 13
      lazer/sdk/js/src/socket/websocket-pool.ts
  43. 2 2
      lazer/sdk/rust/client/Cargo.toml
  44. 1 1
      lazer/sdk/rust/protocol/Cargo.toml
  45. 160 36
      lazer/sdk/rust/protocol/src/api.rs
  46. 1 0
      lazer/sdk/rust/protocol/src/lib.rs
  47. 4 1
      lazer/sdk/rust/protocol/src/time.rs
  48. 4 4
      packages/component-library/src/Skeleton/index.module.scss
  49. 6 1
      packages/component-library/src/Skeleton/index.stories.tsx
  50. 20 14
      packages/component-library/src/Skeleton/index.tsx
  51. 240 83
      pnpm-lock.yaml

+ 13 - 13
Cargo.lock

@@ -5656,7 +5656,7 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-agent"
-version = "0.5.0"
+version = "0.5.1"
 dependencies = [
  "anyhow",
  "backoff",
@@ -5674,8 +5674,8 @@ dependencies = [
  "hyper 1.6.0",
  "hyper-util",
  "protobuf",
- "pyth-lazer-protocol 0.14.0",
- "pyth-lazer-publisher-sdk 0.10.0",
+ "pyth-lazer-protocol 0.15.2",
+ "pyth-lazer-publisher-sdk 0.12.2",
  "reqwest 0.12.23",
  "serde",
  "serde_json",
@@ -5693,7 +5693,7 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-client"
-version = "8.0.1"
+version = "8.2.0"
 dependencies = [
  "alloy-primitives 0.8.25",
  "anyhow",
@@ -5711,7 +5711,7 @@ dependencies = [
  "hex",
  "humantime-serde",
  "libsecp256k1 0.7.2",
- "pyth-lazer-protocol 0.15.1",
+ "pyth-lazer-protocol 0.16.0",
  "reqwest 0.12.23",
  "serde",
  "serde_json",
@@ -5725,9 +5725,9 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-protocol"
-version = "0.14.0"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91b3e69c264b2ad80b5943df86c606daae63b13f93062abcc008c09a9e2e621e"
+checksum = "2ca6d92d185fa8e16521b4ac044fdbd3c4cd492658ab0dc3b417e29e0989e035"
 dependencies = [
  "anyhow",
  "byteorder",
@@ -5746,7 +5746,7 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-protocol"
-version = "0.15.1"
+version = "0.16.0"
 dependencies = [
  "alloy-primitives 0.8.25",
  "anyhow",
@@ -5772,27 +5772,27 @@ dependencies = [
 
 [[package]]
 name = "pyth-lazer-publisher-sdk"
-version = "0.10.0"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98f83b818450d72f6f6db5a9d98e90d2668971da14363820829998290d913f80"
+checksum = "c6e6ce3b7384086c8738d79d22f46b29c7914ab870d3647da43497aea3534f04"
 dependencies = [
  "anyhow",
  "fs-err",
  "protobuf",
  "protobuf-codegen",
- "pyth-lazer-protocol 0.14.0",
+ "pyth-lazer-protocol 0.15.2",
  "serde_json",
 ]
 
 [[package]]
 name = "pyth-lazer-publisher-sdk"
-version = "0.12.1"
+version = "0.13.0"
 dependencies = [
  "anyhow",
  "fs-err",
  "protobuf",
  "protobuf-codegen",
- "pyth-lazer-protocol 0.15.1",
+ "pyth-lazer-protocol 0.16.0",
  "serde_json",
 ]
 

+ 1 - 1
apps/hip-3-pusher/Dockerfile

@@ -30,4 +30,4 @@ RUN --mount=type=cache,target=/root/.cache/uv \
     uv sync --locked --no-dev
 
 # Run the app by default
-CMD ["uv", "run", "src/main.py", "--config", "config/config.toml"]
+CMD ["uv", "run", "-m", "pusher.main", "--config", "config/config.toml"]

+ 9 - 0
apps/hip-3-pusher/README.md

@@ -0,0 +1,9 @@
+# HIP-3 Pusher
+
+`hip-3-pusher` is intended to serve as an oracle updater for
+[HIP-3 markets](https://hyperliquid.gitbook.io/hyperliquid-docs/hyperliquid-improvement-proposals-hips/hip-3-builder-deployed-perpetuals). 
+
+Currently it:
+- Sources market data from Hyperliquid, Pyth Lazer, and Pythnet
+- Supports KMS for signing oracle updates
+- Provides telemetry to Pyth's internal observability system

+ 4 - 1
apps/hip-3-pusher/config/config.toml

@@ -2,6 +2,7 @@ stale_price_threshold_seconds = 5
 prometheus_port = 9090
 
 [hyperliquid]
+hyperliquid_ws_urls = ["wss://api.hyperliquid-testnet.xyz/ws"]
 market_name = ""
 market_symbol = "BTC"
 use_testnet = false
@@ -11,7 +12,9 @@ enable_publish = false
 
 [kms]
 enable_kms = false
-key_path = "/path/to/kms_key.txt"
+key_path = "/path/to/aws_kms_key_id.txt"
+access_key_id_path = "/path/to/aws_access_key_id.txt"
+secret_access_key_path = "/path/to/aws_secret_access_key.txt"
 aws_region_name = "ap-northeast-1"
 
 [lazer]

+ 28 - 12
apps/hip-3-pusher/pyproject.toml

@@ -1,18 +1,34 @@
 [project]
 name = "hip-3-pusher"
-version = "0.1.3"
+version = "0.1.4"
 description = "Hyperliquid HIP-3 market oracle pusher"
 readme = "README.md"
-requires-python = ">=3.13"
+requires-python = "==3.13.*"
 dependencies = [
-    "asn1crypto>=1.5.1",
-    "boto3>=1.40.31",
-    "cryptography>=45.0.7",
-    "hyperliquid-python-sdk>=0.19.0",
-    "loguru>=0.7.3",
-    "opentelemetry-exporter-prometheus>=0.58b0",
-    "opentelemetry-sdk>=1.37.0",
-    "prometheus-client>=0.23.1",
-    "toml>=0.10.2",
-    "websockets>=15.0.1",
+    "boto3~=1.40.38",
+    "cryptography~=46.0.1",
+    "hyperliquid-python-sdk~=0.19.0",
+    "loguru~=0.7.3",
+    "opentelemetry-exporter-prometheus~=0.58b0",
+    "opentelemetry-sdk~=1.37.0",
+    "prometheus-client~=0.23.1",
+    "setuptools~=80.9",
+    "tenacity~=9.1.2",
+    "websockets~=15.0.1",
+    "wheel~=0.45.1",
+]
+
+[build-system]
+requires = ["setuptools~=80.9", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools]
+package-dir = {"" = "src"}
+
+[tool.setuptools.packages.find]
+where = ["src"]
+
+[dependency-groups]
+dev = [
+    "pytest~=8.4.2",
 ]

+ 0 - 35
apps/hip-3-pusher/src/hyperliquid_listener.py

@@ -1,35 +0,0 @@
-from loguru import logger
-import time
-
-from hyperliquid.info import Info
-from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
-
-from price_state import PriceState
-
-
-class HyperliquidListener:
-    """
-    Subscribe to any relevant Hyperliquid websocket streams
-    See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket
-    """
-    def __init__(self, config: dict, price_state: PriceState):
-        self.market_symbol = config["hyperliquid"]["market_symbol"]
-        url = TESTNET_API_URL if config["hyperliquid"].get("use_testnet", True) else MAINNET_API_URL
-        self.info = Info(base_url=url)
-        self.price_state = price_state
-
-    def subscribe(self):
-        self.info.subscribe({"type": "activeAssetCtx", "coin": self.market_symbol}, self.on_activeAssetCtx)
-
-    def on_activeAssetCtx(self, message):
-        """
-        Parse oraclePx and markPx from perp context update
-
-        :param message: activeAssetCtx websocket update message
-        :return: None
-        """
-        ctx = message["data"]["ctx"]
-        self.price_state.hl_oracle_price = ctx["oraclePx"]
-        self.price_state.hl_mark_price = ctx["markPx"]
-        logger.debug("on_activeAssetCtx: oraclePx: {} marketPx: {}", self.price_state.hl_oracle_price, self.price_state.hl_mark_price)
-        self.price_state.latest_hl_timestamp = time.time()

+ 0 - 93
apps/hip-3-pusher/src/kms_signer.py

@@ -1,93 +0,0 @@
-import boto3
-from asn1crypto import core
-from eth_account.messages import encode_typed_data, _hash_eip191_message
-from eth_keys.datatypes import Signature
-from eth_utils import keccak, to_hex
-from hyperliquid.exchange import Exchange
-from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
-from hyperliquid.utils.signing import get_timestamp_ms, action_hash, construct_phantom_agent, l1_payload
-from loguru import logger
-
-
-class KMSSigner:
-    def __init__(self, key_id, aws_region_name, use_testnet):
-        url = TESTNET_API_URL if use_testnet else MAINNET_API_URL
-        self.oracle_publisher_exchange: Exchange = Exchange(wallet=None, base_url=url)
-
-        self.key_id = key_id
-        self.client = boto3.client("kms", region_name=aws_region_name)
-        # Fetch public key once so we can derive address and check recovery id
-        pub_der = self.client.get_public_key(KeyId=key_id)["PublicKey"]
-
-        from cryptography.hazmat.primitives import serialization
-        pub = serialization.load_der_public_key(pub_der)
-        numbers = pub.public_numbers()
-        x = numbers.x.to_bytes(32, "big")
-        y = numbers.y.to_bytes(32, "big")
-        uncompressed = b"\x04" + x + y
-        self.public_key_bytes = uncompressed
-        self.address = "0x" + keccak(uncompressed[1:])[-20:].hex()
-        logger.info("KMSSigner address: {}", self.address)
-
-    def set_oracle(self, dex, oracle_pxs, all_mark_pxs, external_perp_pxs):
-        timestamp = get_timestamp_ms()
-        oracle_pxs_wire = sorted(list(oracle_pxs.items()))
-        mark_pxs_wire = [sorted(list(mark_pxs.items())) for mark_pxs in all_mark_pxs]
-        external_perp_pxs_wire = sorted(list(external_perp_pxs.items()))
-        action = {
-            "type": "perpDeploy",
-            "setOracle": {
-                "dex": dex,
-                "oraclePxs": oracle_pxs_wire,
-                "markPxs": mark_pxs_wire,
-                "externalPerpPxs": external_perp_pxs_wire,
-            },
-        }
-        signature = self.sign_l1_action(
-            action,
-            timestamp,
-            self.oracle_publisher_exchange.base_url == MAINNET_API_URL,
-        )
-        return self.oracle_publisher_exchange._post_action(
-            action,
-            signature,
-            timestamp,
-        )
-
-    def sign_l1_action(self, action, nonce, is_mainnet):
-        hash = action_hash(action, vault_address=None, nonce=nonce, expires_after=None)
-        phantom_agent = construct_phantom_agent(hash, is_mainnet)
-        data = l1_payload(phantom_agent)
-        structured_data = encode_typed_data(full_message=data)
-        message_hash = _hash_eip191_message(structured_data)
-        signed = self.sign_message(message_hash)
-        return {"r": to_hex(signed["r"]), "s": to_hex(signed["s"]), "v": signed["v"]}
-
-    def sign_message(self, message_hash: bytes):
-        resp = self.client.sign(
-            KeyId=self.key_id,
-            Message=message_hash,
-            MessageType="DIGEST",
-            SigningAlgorithm="ECDSA_SHA_256",  # required for secp256k1
-        )
-        der_sig = resp["Signature"]
-
-        seq = core.Sequence.load(der_sig)
-        r = int(seq[0].native)
-        s = int(seq[1].native)
-
-        for recovery_id in (0, 1):
-            candidate = Signature(vrs=(recovery_id, r, s))
-            pubkey = candidate.recover_public_key_from_msg_hash(message_hash)
-            if pubkey.to_bytes() == self.public_key_bytes:
-                v = recovery_id + 27
-                break
-        else:
-            raise ValueError("Failed to determine recovery id")
-
-        return {
-            "r": r,
-            "s": s,
-            "v": v,
-            "signature": Signature(vrs=(v, r, s)).to_bytes().hex(),
-        }

+ 0 - 73
apps/hip-3-pusher/src/price_state.py

@@ -1,73 +0,0 @@
-from loguru import logger
-import time
-
-DEFAULT_STALE_PRICE_THRESHOLD_SECONDS = 5
-
-
-class PriceState:
-    """
-    Maintain latest prices seen across listeners and publisher.
-    """
-    def __init__(self, config):
-        self.stale_price_threshold_seconds = config.get("stale_price_threshold_seconds", DEFAULT_STALE_PRICE_THRESHOLD_SECONDS)
-        now = time.time()
-
-        self.hl_oracle_price = None
-        self.hl_mark_price = None
-        self.latest_hl_timestamp = now
-
-        self.lazer_base_price = None
-        self.lazer_base_exponent = config["lazer"]["base_feed_exponent"]
-        self.lazer_quote_price = None
-        self.lazer_quote_exponent = config["lazer"]["quote_feed_exponent"]
-        self.latest_lazer_timestamp = now
-
-        self.hermes_base_price = None
-        self.hermes_base_exponent = config["hermes"]["base_feed_exponent"]
-        self.hermes_quote_price = None
-        self.hermes_quote_exponent = config["hermes"]["quote_feed_exponent"]
-        self.latest_hermes_timestamp = now
-
-    def get_current_oracle_price(self):
-        now = time.time()
-        if self.hl_oracle_price:
-            time_diff = now - self.latest_hl_timestamp
-            if time_diff < self.stale_price_threshold_seconds:
-                return self.hl_oracle_price
-            else:
-                logger.error("Hyperliquid oracle price stale by {} seconds", time_diff)
-        else:
-            logger.error("Hyperliquid oracle price not received yet")
-
-        # fall back to Hermes
-        if self.hermes_base_price and self.hermes_quote_price:
-            time_diff = now - self.latest_hermes_timestamp
-            if time_diff < self.stale_price_threshold_seconds:
-                return self.get_hermes_price()
-            else:
-                logger.error("Hermes price stale by {} seconds", time_diff)
-        else:
-            logger.error("Hermes base/quote prices not received yet")
-
-        # fall back to Lazer
-        if self.lazer_base_price and self.lazer_quote_price:
-            time_diff = now - self.latest_lazer_timestamp
-            if time_diff < self.stale_price_threshold_seconds:
-                return self.get_lazer_price()
-            else:
-                logger.error("Lazer price stale by {} seconds", time_diff)
-        else:
-            logger.error("Lazer base/quote prices not received yet")
-
-        logger.error("All prices missing or stale!")
-        return None
-
-    def get_hermes_price(self):
-        base_price = float(self.hermes_base_price) / (10.0 ** -self.hermes_base_exponent)
-        quote_price = float(self.hermes_quote_price) / (10.0 ** -self.hermes_quote_exponent)
-        return str(round(base_price / quote_price, 2))
-
-    def get_lazer_price(self):
-        base_price = float(self.lazer_base_price) / (10.0 ** -self.lazer_base_exponent)
-        quote_price = float(self.lazer_quote_price) / (10.0 ** -self.lazer_quote_exponent)
-        return str(round(base_price / quote_price, 2))

+ 47 - 0
apps/hip-3-pusher/src/pusher/config.py

@@ -0,0 +1,47 @@
+from pydantic import BaseModel
+
+STALE_TIMEOUT_SECONDS = 5
+
+
+class KMSConfig(BaseModel):
+    enable_kms: bool
+    aws_region_name: str
+    key_path: str
+    access_key_id_path: str
+    secret_access_key_path: str
+
+
+class LazerConfig(BaseModel):
+    lazer_urls: list[str]
+    lazer_api_key: str
+    base_feed_id: int
+    base_feed_exponent: int
+    quote_feed_id: int
+    quote_feed_exponent: int
+
+
+class HermesConfig(BaseModel):
+    hermes_urls: list[str]
+    base_feed_id: str
+    base_feed_exponent: int
+    quote_feed_id: str
+    quote_feed_exponent: int
+
+
+class HyperliquidConfig(BaseModel):
+    hyperliquid_ws_urls: list[str]
+    market_name: str
+    market_symbol: str
+    use_testnet: bool
+    oracle_pusher_key_path: str
+    publish_interval: float
+    enable_publish: bool
+
+
+class Config(BaseModel):
+    stale_price_threshold_seconds: int
+    prometheus_port: int
+    hyperliquid: HyperliquidConfig
+    kms: KMSConfig
+    lazer: LazerConfig
+    hermes: HermesConfig

+ 2 - 0
apps/hip-3-pusher/src/pusher/exception.py

@@ -0,0 +1,2 @@
+class StaleConnection(Exception):
+    pass

+ 25 - 16
apps/hip-3-pusher/src/hermes_listener.py → apps/hip-3-pusher/src/pusher/hermes_listener.py

@@ -3,18 +3,21 @@ import json
 from loguru import logger
 import time
 import websockets
+from tenacity import retry, retry_if_exception_type, wait_exponential
 
-from price_state import PriceState
+from pusher.config import Config, STALE_TIMEOUT_SECONDS
+from pusher.exception import StaleConnection
+from pusher.price_state import PriceState, PriceUpdate
 
 
 class HermesListener:
     """
     Subscribe to Hermes price updates for needed feeds.
     """
-    def __init__(self, config, price_state: PriceState):
-        self.hermes_urls = config["hermes"]["hermes_urls"]
-        self.base_feed_id = config["hermes"]["base_feed_id"]
-        self.quote_feed_id = config["hermes"]["quote_feed_id"]
+    def __init__(self, config: Config, price_state: PriceState):
+        self.hermes_urls = config.hermes.hermes_urls
+        self.base_feed_id = config.hermes.base_feed_id
+        self.quote_feed_id = config.hermes.quote_feed_id
         self.price_state = price_state
 
     def get_subscribe_request(self):
@@ -30,14 +33,13 @@ class HermesListener:
     async def subscribe_all(self):
         await asyncio.gather(*(self.subscribe_single(url) for url in self.hermes_urls))
 
+    @retry(
+        retry=retry_if_exception_type((StaleConnection, websockets.ConnectionClosed)),
+        wait=wait_exponential(multiplier=1, min=1, max=10),
+        reraise=True,
+    )
     async def subscribe_single(self, url):
-        while True:
-            try:
-                await self.subscribe_single_inner(url)
-            except websockets.ConnectionClosed:
-                logger.error("Connection to {} closed; retrying", url)
-            except Exception as e:
-                logger.exception("Error on {}: {}", url, e)
+        return await self.subscribe_single_inner(url)
 
     async def subscribe_single_inner(self, url):
         async with websockets.connect(url) as ws:
@@ -47,12 +49,19 @@ class HermesListener:
             logger.info("Sent Hermes subscribe request to {}", url)
 
             # listen for updates
-            async for message in ws:
+            while True:
                 try:
+                    message = await asyncio.wait_for(ws.recv(), timeout=STALE_TIMEOUT_SECONDS)
                     data = json.loads(message)
                     self.parse_hermes_message(data)
+                except asyncio.TimeoutError:
+                    raise StaleConnection(f"No messages in {STALE_TIMEOUT_SECONDS} seconds, reconnecting")
+                except websockets.ConnectionClosed:
+                    raise
                 except json.JSONDecodeError as e:
                     logger.error("Failed to decode JSON message: {}", e)
+                except Exception as e:
+                    logger.error("Unexpected exception: {}", e)
 
     def parse_hermes_message(self, data):
         """
@@ -71,10 +80,10 @@ class HermesListener:
             expo = price_object["expo"]
             publish_time = price_object["publish_time"]
             logger.debug("Hermes update: {} {} {} {}", id, price, expo, publish_time)
+            now = time.time()
             if id == self.base_feed_id:
-                self.price_state.hermes_base_price = price
+                self.price_state.hermes_base_price = PriceUpdate(price, now)
             if id == self.quote_feed_id:
-                self.price_state.hermes_quote_price = price
-            self.price_state.latest_hermes_timestamp = time.time()
+                self.price_state.hermes_quote_price = PriceUpdate(price, now)
         except Exception as e:
             logger.error("parse_hermes_message error: {}", e)

+ 85 - 0
apps/hip-3-pusher/src/pusher/hyperliquid_listener.py

@@ -0,0 +1,85 @@
+import asyncio
+import json
+import websockets
+from loguru import logger
+from tenacity import retry, retry_if_exception_type, wait_exponential
+import time
+
+from pusher.config import Config, STALE_TIMEOUT_SECONDS
+from pusher.exception import StaleConnection
+from pusher.price_state import PriceState, PriceUpdate
+
+# This will be in config, but note here.
+# Other RPC providers exist but so far we've seen their support is incomplete.
+HYPERLIQUID_MAINNET_WS_URL = "wss://api.hyperliquid.xyz/ws"
+HYPERLIQUID_TESTNET_WS_URL = "wss://api.hyperliquid-testnet.xyz/ws"
+
+
+class HyperliquidListener:
+    """
+    Subscribe to any relevant Hyperliquid websocket streams
+    See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket
+    """
+    def __init__(self, config: Config, price_state: PriceState):
+        self.hyperliquid_ws_urls = config.hyperliquid.hyperliquid_ws_urls
+        self.market_symbol = config.hyperliquid.market_symbol
+        self.price_state = price_state
+
+    def get_subscribe_request(self, asset):
+        return {
+            "method": "subscribe",
+            "subscription": {"type": "activeAssetCtx", "coin": asset}
+        }
+
+    async def subscribe_all(self):
+        await asyncio.gather(*(self.subscribe_single(hyperliquid_ws_url) for hyperliquid_ws_url in self.hyperliquid_ws_urls))
+
+    @retry(
+        retry=retry_if_exception_type((StaleConnection, websockets.ConnectionClosed)),
+        wait=wait_exponential(multiplier=1, min=1, max=10),
+        reraise=True,
+    )
+    async def subscribe_single(self, url):
+        return await self.subscribe_single_inner(url)
+
+    async def subscribe_single_inner(self, url):
+        async with websockets.connect(url) as ws:
+            subscribe_request = self.get_subscribe_request(self.market_symbol)
+            await ws.send(json.dumps(subscribe_request))
+            logger.info("Sent subscribe request to {}", url)
+
+            # listen for updates
+            while True:
+                try:
+                    message = await asyncio.wait_for(ws.recv(), timeout=STALE_TIMEOUT_SECONDS)
+                    data = json.loads(message)
+                    channel = data.get("channel", None)
+                    if not channel:
+                        logger.error("No channel in message: {}", data)
+                    elif channel == "subscriptionResponse":
+                        logger.debug("Received subscription response: {}", data)
+                    elif channel == "error":
+                        logger.error("Received Hyperliquid error response: {}", data)
+                    elif channel == "activeAssetCtx":
+                        self.parse_hyperliquid_ws_message(data)
+                    else:
+                        logger.error("Received unknown channel: {}", channel)
+                except asyncio.TimeoutError:
+                    raise StaleConnection(f"No messages in {STALE_TIMEOUT_SECONDS} seconds, reconnecting...")
+                except websockets.ConnectionClosed:
+                    raise
+                except json.JSONDecodeError as e:
+                    logger.error("Failed to decode JSON message: {} error: {}", message, e)
+                except Exception as e:
+                    logger.error("Unexpected exception: {}", e)
+
+    def parse_hyperliquid_ws_message(self, message):
+        try:
+            ctx = message["data"]["ctx"]
+            now = time.time()
+            self.price_state.hl_oracle_price = PriceUpdate(ctx["oraclePx"], now)
+            self.price_state.hl_mark_price = PriceUpdate(ctx["markPx"], now)
+            logger.debug("on_activeAssetCtx: oraclePx: {} marketPx: {}", self.price_state.hl_oracle_price,
+                         self.price_state.hl_mark_price)
+        except Exception as e:
+            logger.error("parse_hyperliquid_ws_message error: message: {} e: {}", message, e)

+ 121 - 0
apps/hip-3-pusher/src/pusher/kms_signer.py

@@ -0,0 +1,121 @@
+import boto3
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
+from eth_account.messages import encode_typed_data, _hash_eip191_message
+from eth_keys.backends.native.ecdsa import N as SECP256K1_N
+from eth_keys.datatypes import Signature
+from eth_utils import keccak, to_hex
+from hyperliquid.exchange import Exchange
+from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
+from hyperliquid.utils.signing import get_timestamp_ms, action_hash, construct_phantom_agent, l1_payload
+from loguru import logger
+
+from pusher.config import Config
+
+SECP256K1_N_HALF = SECP256K1_N // 2
+
+
+class KMSSigner:
+    def __init__(self, config: Config):
+        use_testnet = config.hyperliquid.use_testnet
+        url = TESTNET_API_URL if use_testnet else MAINNET_API_URL
+        self.oracle_publisher_exchange: Exchange = Exchange(wallet=None, base_url=url)
+        self.client = self._init_client(config)
+
+        # Fetch public key once so we can derive address and check recovery id
+        key_path = config.kms.key_path
+        self.key_id = open(key_path, "r").read().strip()
+        self.pubkey_der = self.client.get_public_key(KeyId=self.key_id)["PublicKey"]
+        # Construct eth address to log
+        pub = serialization.load_der_public_key(self.pubkey_der)
+        numbers = pub.public_numbers()
+        x = numbers.x.to_bytes(32, "big")
+        y = numbers.y.to_bytes(32, "big")
+        uncompressed = b"\x04" + x + y
+        self.public_key_bytes = uncompressed
+        self.address = "0x" + keccak(uncompressed[1:])[-20:].hex()
+        logger.info("KMSSigner address: {}", self.address)
+
+    def _init_client(self, config):
+        aws_region_name = config.kms.aws_region_name
+        access_key_id_path = config.kms.access_key_id_path
+        access_key_id = open(access_key_id_path, "r").read().strip()
+        secret_access_key_path = config.kms.secret_access_key_path
+        secret_access_key = open(secret_access_key_path, "r").read().strip()
+
+        return boto3.client(
+            "kms",
+            region_name=aws_region_name,
+            aws_access_key_id=access_key_id,
+            aws_secret_access_key=secret_access_key,
+            # can specify an endpoint for e.g. LocalStack
+            # endpoint_url="http://localhost:4566"
+        )
+
+    def set_oracle(self, dex, oracle_pxs, all_mark_pxs, external_perp_pxs):
+        timestamp = get_timestamp_ms()
+        oracle_pxs_wire = sorted(list(oracle_pxs.items()))
+        mark_pxs_wire = [sorted(list(mark_pxs.items())) for mark_pxs in all_mark_pxs]
+        external_perp_pxs_wire = sorted(list(external_perp_pxs.items()))
+        action = {
+            "type": "perpDeploy",
+            "setOracle": {
+                "dex": dex,
+                "oraclePxs": oracle_pxs_wire,
+                "markPxs": mark_pxs_wire,
+                "externalPerpPxs": external_perp_pxs_wire,
+            },
+        }
+        signature = self.sign_l1_action(
+            action,
+            timestamp,
+            self.oracle_publisher_exchange.base_url == MAINNET_API_URL,
+        )
+        return self.oracle_publisher_exchange._post_action(
+            action,
+            signature,
+            timestamp,
+        )
+
+    def sign_l1_action(self, action, nonce, is_mainnet):
+        hash = action_hash(action, vault_address=None, nonce=nonce, expires_after=None)
+        phantom_agent = construct_phantom_agent(hash, is_mainnet)
+        data = l1_payload(phantom_agent)
+        structured_data = encode_typed_data(full_message=data)
+        message_hash = _hash_eip191_message(structured_data)
+        return self.sign_message(message_hash)
+
+    def sign_message(self, message_hash: bytes) -> dict:
+        # Send message hash to KMS for signing
+        resp = self.client.sign(
+            KeyId=self.key_id,
+            Message=message_hash,
+            MessageType="DIGEST",
+            SigningAlgorithm="ECDSA_SHA_256",  # required for secp256k1
+        )
+        kms_signature = resp["Signature"]
+        # Decode the KMS DER signature -> (r, s)
+        r, s = decode_dss_signature(kms_signature)
+        # Ethereum requires low-s form
+        if s > SECP256K1_N_HALF:
+            s = SECP256K1_N - s
+        # Parse KMS public key into uncompressed secp256k1 bytes
+        # TODO: Pull this into init
+        pubkey = serialization.load_der_public_key(self.pubkey_der)
+        pubkey_bytes = pubkey.public_bytes(
+            serialization.Encoding.X962,
+            serialization.PublicFormat.UncompressedPoint,
+        )
+        # Strip leading 0x04 (uncompressed point indicator)
+        raw_pubkey_bytes = pubkey_bytes[1:]
+        # Try both recovery ids
+        for v in (0, 1):
+            sig_obj = Signature(vrs=(v, r, s))
+            recovered_pub = sig_obj.recover_public_key_from_msg_hash(message_hash)
+            if recovered_pub.to_bytes() == raw_pubkey_bytes:
+                return {
+                    "r": to_hex(r),
+                    "s": to_hex(s),
+                    "v": v + 27,
+                }
+        raise ValueError("Could not recover public key; signature mismatch")

+ 27 - 18
apps/hip-3-pusher/src/lazer_listener.py → apps/hip-3-pusher/src/pusher/lazer_listener.py

@@ -3,19 +3,22 @@ import json
 from loguru import logger
 import time
 import websockets
+from tenacity import retry, retry_if_exception_type, wait_exponential
 
-from price_state import PriceState
+from pusher.config import Config, STALE_TIMEOUT_SECONDS
+from pusher.exception import StaleConnection
+from pusher.price_state import PriceState, PriceUpdate
 
 
 class LazerListener:
     """
     Subscribe to Lazer price updates for needed feeds.
     """
-    def __init__(self, config, price_state: PriceState):
-        self.lazer_urls = config["lazer"]["lazer_urls"]
-        self.api_key = config["lazer"]["lazer_api_key"]
-        self.base_feed_id = config["lazer"]["base_feed_id"]
-        self.quote_feed_id = config["lazer"]["quote_feed_id"]
+    def __init__(self, config: Config, price_state: PriceState):
+        self.lazer_urls = config.lazer.lazer_urls
+        self.api_key = config.lazer.lazer_api_key
+        self.base_feed_id = config.lazer.base_feed_id
+        self.quote_feed_id = config.lazer.quote_feed_id
         self.price_state = price_state
 
     def get_subscribe_request(self, subscription_id: int):
@@ -34,14 +37,13 @@ class LazerListener:
     async def subscribe_all(self):
         await asyncio.gather(*(self.subscribe_single(router_url) for router_url in self.lazer_urls))
 
+    @retry(
+        retry=retry_if_exception_type((StaleConnection, websockets.ConnectionClosed)),
+        wait=wait_exponential(multiplier=1, min=1, max=10),
+        reraise=True,
+    )
     async def subscribe_single(self, router_url):
-        while True:
-            try:
-                await self.subscribe_single_inner(router_url)
-            except websockets.ConnectionClosed:
-                logger.error("Connection to {} closed; retrying", router_url)
-            except Exception as e:
-                logger.exception("Error on {}: {}", router_url, e)
+        return await self.subscribe_single_inner(router_url)
 
     async def subscribe_single_inner(self, router_url):
         headers = {
@@ -52,15 +54,22 @@ class LazerListener:
             subscribe_request = self.get_subscribe_request(1)
 
             await ws.send(json.dumps(subscribe_request))
-            logger.info("Sent Lazer subscribe request to {}", self.lazer_urls[0])
+            logger.info("Sent Lazer subscribe request to {}", router_url)
 
             # listen for updates
-            async for message in ws:
+            while True:
                 try:
+                    message = await asyncio.wait_for(ws.recv(), timeout=STALE_TIMEOUT_SECONDS)
                     data = json.loads(message)
                     self.parse_lazer_message(data)
+                except asyncio.TimeoutError:
+                    raise StaleConnection(f"No messages in {STALE_TIMEOUT_SECONDS} seconds, reconnecting")
+                except websockets.ConnectionClosed:
+                    raise
                 except json.JSONDecodeError as e:
                     logger.error("Failed to decode JSON message: {}", e)
+                except Exception as e:
+                    logger.error("Unexpected exception: {}", e)
 
     def parse_lazer_message(self, data):
         """
@@ -74,15 +83,15 @@ class LazerListener:
                 return
             price_feeds = data["parsed"]["priceFeeds"]
             logger.debug("price_feeds: {}", price_feeds)
+            now = time.time()
             for feed_update in price_feeds:
                 feed_id = feed_update.get("priceFeedId", None)
                 price = feed_update.get("price", None)
                 if feed_id is None or price is None:
                     continue
                 if feed_id == self.base_feed_id:
-                    self.price_state.lazer_base_price = price
+                    self.price_state.lazer_base_price = PriceUpdate(price, now)
                 if feed_id == self.quote_feed_id:
-                    self.price_state.lazer_quote_price = price
-            self.price_state.latest_lazer_timestamp = time.time()
+                    self.price_state.lazer_quote_price = PriceUpdate(price, now)
         except Exception as e:
             logger.error("parse_lazer_message error: {}", e)

+ 13 - 11
apps/hip-3-pusher/src/main.py → apps/hip-3-pusher/src/pusher/main.py

@@ -3,14 +3,15 @@ import asyncio
 from loguru import logger
 import os
 import sys
-import toml
+import tomllib
 
-from hyperliquid_listener import HyperliquidListener
-from lazer_listener import LazerListener
-from hermes_listener import HermesListener
-from price_state import PriceState
-from publisher import Publisher
-from metrics import Metrics
+from pusher.config import Config
+from pusher.hyperliquid_listener import HyperliquidListener
+from pusher.lazer_listener import LazerListener
+from pusher.hermes_listener import HermesListener
+from pusher.price_state import PriceState
+from pusher.publisher import Publisher
+from pusher.metrics import Metrics
 
 
 def load_config():
@@ -21,8 +22,10 @@ def load_config():
         help="hip3-agent config file",
     )
     config_path = parser.parse_args().config
-    with open(config_path, "r") as config_file:
-        config = toml.load(config_file)
+    with open(config_path, "rb") as config_file:
+        config_toml = tomllib.load(config_file)
+        config = Config(**config_toml)
+        logger.debug("Config loaded: {}", config)
     return config
 
 
@@ -46,10 +49,9 @@ async def main():
     lazer_listener = LazerListener(config, price_state)
     hermes_listener = HermesListener(config, price_state)
 
-    # TODO: Probably pull this out of the sdk so we can handle reconnects.
-    hyperliquid_listener.subscribe()
     await asyncio.gather(
         publisher.run(),
+        hyperliquid_listener.subscribe_all(),
         lazer_listener.subscribe_all(),
         hermes_listener.subscribe_all(),
     )

+ 4 - 2
apps/hip-3-pusher/src/metrics.py → apps/hip-3-pusher/src/pusher/metrics.py

@@ -3,14 +3,16 @@ from opentelemetry.exporter.prometheus import PrometheusMetricReader
 from opentelemetry.metrics import get_meter_provider, set_meter_provider
 from opentelemetry.sdk.metrics import MeterProvider
 
+from pusher.config import Config
+
 METER_NAME = "hip3pusher"
 
 
 class Metrics:
-    def __init__(self, config):
+    def __init__(self, config: Config):
         # Adapted from opentelemetry-exporter-prometheus example code.
         # Start Prometheus client
-        start_http_server(port=config["prometheus_port"])
+        start_http_server(port=config.prometheus_port)
         # Exporter to export metrics to Prometheus
         reader = PrometheusMetricReader()
         # Meter is responsible for creating and recording metrics

+ 83 - 0
apps/hip-3-pusher/src/pusher/price_state.py

@@ -0,0 +1,83 @@
+from loguru import logger
+import time
+
+from pusher.config import Config
+
+DEFAULT_STALE_PRICE_THRESHOLD_SECONDS = 5
+
+
+class PriceUpdate:
+    def __init__(self, price, timestamp):
+        self.price = price
+        self.timestamp = timestamp
+
+    def __str__(self):
+        return f"PriceUpdate(price={self.price}, timestamp={self.timestamp})"
+
+    def time_diff(self, now):
+        return now - self.timestamp
+
+
+class PriceState:
+    """
+    Maintain latest prices seen across listeners and publisher.
+    """
+    def __init__(self, config: Config):
+        self.stale_price_threshold_seconds = config.stale_price_threshold_seconds
+
+        self.hl_oracle_price: PriceUpdate | None = None
+        self.hl_mark_price: PriceUpdate | None = None
+
+        self.lazer_base_price: PriceUpdate | None = None
+        self.lazer_base_exponent = config.lazer.base_feed_exponent
+        self.lazer_quote_price: PriceUpdate | None = None
+        self.lazer_quote_exponent = config.lazer.quote_feed_exponent
+
+        self.hermes_base_price: PriceUpdate | None = None
+        self.hermes_base_exponent = config.hermes.base_feed_exponent
+        self.hermes_quote_price: PriceUpdate | None = None
+        self.hermes_quote_exponent = config.hermes.quote_feed_exponent
+
+    def get_current_oracle_price(self):
+        now = time.time()
+        if self.hl_oracle_price:
+            time_diff = self.hl_oracle_price.time_diff(now)
+            if time_diff < self.stale_price_threshold_seconds:
+                return self.hl_oracle_price.price
+            else:
+                logger.error("Hyperliquid oracle price stale by {} seconds", time_diff)
+        else:
+            logger.error("Hyperliquid oracle price not received yet")
+
+        # fall back to Lazer
+        if self.lazer_base_price and self.lazer_quote_price:
+            max_time_diff = max(self.lazer_base_price.time_diff(now), self.lazer_quote_price.time_diff(now))
+            if max_time_diff < self.stale_price_threshold_seconds:
+                return self.get_lazer_price()
+            else:
+                logger.error("Lazer price stale by {} seconds", max_time_diff)
+        else:
+            logger.error("Lazer base/quote prices not received yet")
+
+        # fall back to Hermes
+        if self.hermes_base_price and self.hermes_quote_price:
+            max_time_diff = max(self.hermes_base_price.time_diff(now), self.hermes_quote_price.time_diff(now))
+            if max_time_diff < self.stale_price_threshold_seconds:
+                return self.get_hermes_price()
+            else:
+                logger.error("Hermes price stale by {} seconds", max_time_diff)
+        else:
+            logger.error("Hermes base/quote prices not received yet")
+
+        logger.error("All prices missing or stale!")
+        return None
+
+    def get_hermes_price(self):
+        base_price = float(self.hermes_base_price.price) / (10.0 ** -self.hermes_base_exponent)
+        quote_price = float(self.hermes_quote_price.price) / (10.0 ** -self.hermes_quote_exponent)
+        return str(round(base_price / quote_price, 2))
+
+    def get_lazer_price(self):
+        base_price = float(self.lazer_base_price.price) / (10.0 ** -self.lazer_base_exponent)
+        quote_price = float(self.lazer_quote_price.price) / (10.0 ** -self.lazer_quote_exponent)
+        return str(round(base_price / quote_price, 2))

+ 15 - 16
apps/hip-3-pusher/src/publisher.py → apps/hip-3-pusher/src/pusher/publisher.py

@@ -1,5 +1,4 @@
 import asyncio
-
 from loguru import logger
 
 from eth_account import Account
@@ -7,9 +6,10 @@ from eth_account.signers.local import LocalAccount
 from hyperliquid.exchange import Exchange
 from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
 
-from kms_signer import KMSSigner
-from metrics import Metrics
-from price_state import PriceState
+from pusher.config import Config
+from pusher.kms_signer import KMSSigner
+from pusher.metrics import Metrics
+from pusher.price_state import PriceState
 
 
 class Publisher:
@@ -18,30 +18,27 @@ class Publisher:
 
     See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/hip-3-deployer-actions
     """
-    def __init__(self, config: dict, price_state: PriceState, metrics: Metrics):
-        self.publish_interval = float(config["hyperliquid"]["publish_interval"])
+    def __init__(self, config: Config, price_state: PriceState, metrics: Metrics):
+        self.publish_interval = float(config.hyperliquid.publish_interval)
         self.kms_signer = None
         self.enable_kms = False
-        self.use_testnet = config["hyperliquid"].get("use_testnet", True)
+        self.use_testnet = config.hyperliquid.use_testnet
 
-        if config["kms"]["enable_kms"]:
+        if config.kms.enable_kms:
             self.enable_kms = True
             oracle_account = None
-            kms_key_path = config["kms"]["key_path"]
-            kms_key_id = open(kms_key_path, "r").read().strip()
-            self.kms_signer = KMSSigner(kms_key_id, config["kms"]["aws_region_name"], self.use_testnet)
+            self.kms_signer = KMSSigner(config)
         else:
-            oracle_pusher_key_path = config["hyperliquid"]["oracle_pusher_key_path"]
+            oracle_pusher_key_path = config.hyperliquid.oracle_pusher_key_path
             oracle_pusher_key = open(oracle_pusher_key_path, "r").read().strip()
             oracle_account: LocalAccount = Account.from_key(oracle_pusher_key)
-            del oracle_pusher_key
             logger.info("oracle pusher local pubkey: {}", oracle_account.address)
 
         url = TESTNET_API_URL if self.use_testnet else MAINNET_API_URL
         self.oracle_publisher_exchange: Exchange = Exchange(wallet=oracle_account, base_url=url)
-        self.market_name = config["hyperliquid"]["market_name"]
-        self.market_symbol = config["hyperliquid"]["market_symbol"]
-        self.enable_publish = config["hyperliquid"].get("enable_publish", False)
+        self.market_name = config.hyperliquid.market_name
+        self.market_symbol = config.hyperliquid.market_symbol
+        self.enable_publish = config.hyperliquid.enable_publish
 
         self.price_state = price_state
         self.metrics = metrics
@@ -70,6 +67,8 @@ class Publisher:
         #    mark_pxs.append({self.market_symbol: self.price_state.hl_mark_price})
 
         external_perp_pxs = {}
+        # TODO: "Each update can change oraclePx and markPx by at most 1%."
+        # TODO: "The markPx cannot be updated such that open interest would be 10x the open interest cap."
 
         if self.enable_publish:
             if self.enable_kms:

+ 70 - 0
apps/hip-3-pusher/tests/test_price_state.py

@@ -0,0 +1,70 @@
+import time
+
+from pusher.config import Config, LazerConfig, HermesConfig
+from pusher.price_state import PriceState, PriceUpdate
+
+
+def get_config():
+    config: Config = Config.model_construct()
+    config.stale_price_threshold_seconds = 5
+    config.lazer = LazerConfig.model_construct()
+    config.lazer.base_feed_exponent = -8
+    config.lazer.quote_feed_exponent = -8
+    config.hermes = HermesConfig.model_construct()
+    config.hermes.base_feed_exponent = -8
+    config.hermes.quote_feed_exponent = -8
+    return config
+
+
+def test_good_hl_price():
+    config = get_config()
+    price_state = PriceState(config)
+    now = time.time()
+    price_state.hl_oracle_price = PriceUpdate("110000.0", now - price_state.stale_price_threshold_seconds / 2.0)
+
+    oracle_px = price_state.get_current_oracle_price()
+    assert oracle_px == price_state.hl_oracle_price.price
+    assert oracle_px == "110000.0"
+
+
+
+def test_fallback_lazer():
+    config = get_config()
+    price_state = PriceState(config)
+    now = time.time()
+    price_state.hl_oracle_price = PriceUpdate("110000.0", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.lazer_base_price = PriceUpdate("11050000000000", now - price_state.stale_price_threshold_seconds / 2.0)
+    price_state.lazer_quote_price = PriceUpdate("99000000", now - price_state.stale_price_threshold_seconds / 2.0)
+
+    oracle_px = price_state.get_current_oracle_price()
+    assert oracle_px == price_state.get_lazer_price()
+    assert oracle_px == "111616.16"
+
+
+
+def test_fallback_hermes():
+    config = get_config()
+    price_state = PriceState(config)
+    now = time.time()
+    price_state.hl_oracle_price = PriceUpdate("110000.0", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.lazer_base_price = PriceUpdate("11050000000000", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.lazer_quote_price = PriceUpdate("99000000", now - price_state.stale_price_threshold_seconds / 2.0)
+    price_state.hermes_base_price = PriceUpdate("11100000000000", now - price_state.stale_price_threshold_seconds / 2.0)
+    price_state.hermes_quote_price = PriceUpdate("98000000", now - price_state.stale_price_threshold_seconds / 2.0)
+
+    oracle_px = price_state.get_current_oracle_price()
+    assert oracle_px == price_state.get_hermes_price()
+    assert oracle_px == "113265.31"
+
+
+def test_all_fail():
+    config = get_config()
+    price_state = PriceState(config)
+    now = time.time()
+    price_state.hl_oracle_price = PriceUpdate("110000.0", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.hl_oracle_price = PriceUpdate("110000.0", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.lazer_base_price = PriceUpdate("11050000000000", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.lazer_quote_price = PriceUpdate("99000000", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.hermes_base_price = PriceUpdate("11100000000000", now - price_state.stale_price_threshold_seconds - 1.0)
+    price_state.hermes_quote_price = PriceUpdate("98000000", now - price_state.stale_price_threshold_seconds - 1.0)
+    assert price_state.get_current_oracle_price() is None

+ 138 - 108
apps/hip-3-pusher/uv.lock

@@ -1,6 +1,6 @@
 version = 1
 revision = 3
-requires-python = ">=3.13"
+requires-python = "==3.13.*"
 
 [[package]]
 name = "annotated-types"
@@ -11,15 +11,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
 ]
 
-[[package]]
-name = "asn1crypto"
-version = "1.5.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" },
-]
-
 [[package]]
 name = "bitarray"
 version = "3.7.1"
@@ -44,30 +35,30 @@ wheels = [
 
 [[package]]
 name = "boto3"
-version = "1.40.31"
+version = "1.40.38"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "botocore" },
     { name = "jmespath" },
     { name = "s3transfer" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/9c/e6/1a8b1710245aac792ea283051727f4e7f29b4bfb85fd887f4c25aa85b4eb/boto3-1.40.31.tar.gz", hash = "sha256:8c5f1270f09431694412f326cfb5ac9786d41ea3bc6ac54cbb7161d40afc660d", size = 111606, upload-time = "2025-09-15T19:38:46.136Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/8d/c7/1442380ad7e211089a3c94b758ffb01079eab0183700fba9d5be417b5cb4/boto3-1.40.38.tar.gz", hash = "sha256:932ebdd8dbf8ab5694d233df86d5d0950291e0b146c27cb46da8adb4f00f6ca4", size = 111559, upload-time = "2025-09-24T19:23:25.7Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/ee/79/22fd4a4a61cb87f893804d8a36a834c69849a8fe2b0d9e28c2676824bff1/boto3-1.40.31-py3-none-any.whl", hash = "sha256:61d5f9975c54ff919a24ff9d472c6c09c8a443a083fe56d30c04fc22d22ac42b", size = 139343, upload-time = "2025-09-15T19:38:44.707Z" },
+    { url = "https://files.pythonhosted.org/packages/06/a9/e7e5fe3fec60fb87bc9f8b3874c4c606e290a64b2ae8c157e08c3e69d755/boto3-1.40.38-py3-none-any.whl", hash = "sha256:fac337b4f0615e4d6ceee44686e662f51d8e57916ed2bc763468e3e8c611a658", size = 139345, upload-time = "2025-09-24T19:23:23.756Z" },
 ]
 
 [[package]]
 name = "botocore"
-version = "1.40.31"
+version = "1.40.38"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "jmespath" },
     { name = "python-dateutil" },
     { name = "urllib3" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/42/8a/6ed2ccd9f6b8930ed15e755667f8f5f32a568cf4e3bf31a8526dcf4ce562/botocore-1.40.31.tar.gz", hash = "sha256:9496b91bbe40ed01d341dc8f6ff0492d7f546e80f1a862b00ec5bc9045fa3324", size = 14340752, upload-time = "2025-09-15T19:38:35.659Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/11/82a216e24f1af1ba5c3c358201fb9eba5e502242f504dd1f42eb18cbf2c5/botocore-1.40.38.tar.gz", hash = "sha256:18039009e1eca2bff12e576e8dd3c80cd9b312294f1469c831de03169582ad59", size = 14354395, upload-time = "2025-09-24T19:23:14.522Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/42/98/0f6cfbc627a87d84cfc66ceb7d9b71d694313a32cddbe415b9f298c9e50b/botocore-1.40.31-py3-none-any.whl", hash = "sha256:4033a00f8c6a4b5c3acb30af9283963123917227a437e5fc165189d07bd3cf8a", size = 14013342, upload-time = "2025-09-15T19:38:32.098Z" },
+    { url = "https://files.pythonhosted.org/packages/e4/f0/ca5a00dd8fe3768ecff54756457dd0c69ed8e1cd09d0f7c21599477b5d5b/botocore-1.40.38-py3-none-any.whl", hash = "sha256:7d60a7557db3a58f9394e7ecec1f6b87495ce947eb713f29d53aee83a6e9dc71", size = 14025193, upload-time = "2025-09-24T19:23:11.093Z" },
 ]
 
 [[package]]
@@ -100,28 +91,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
     { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
     { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
-    { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
-    { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
-    { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
-    { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
-    { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
-    { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
-    { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
-    { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
-    { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
-    { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
-    { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
-    { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
-    { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
-    { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
-    { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
-    { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
-    { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
-    { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
-    { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
-    { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
-    { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
-    { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
 ]
 
 [[package]]
@@ -141,17 +110,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
     { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
     { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
-    { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
-    { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
-    { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
-    { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
-    { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
-    { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
-    { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
-    { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
-    { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
-    { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
-    { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
     { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
 ]
 
@@ -183,37 +141,43 @@ wheels = [
 
 [[package]]
 name = "cryptography"
-version = "45.0.7"
+version = "46.0.1"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980, upload-time = "2025-09-01T11:15:03.146Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105, upload-time = "2025-09-01T11:13:59.684Z" },
-    { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799, upload-time = "2025-09-01T11:14:02.517Z" },
-    { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504, upload-time = "2025-09-01T11:14:04.522Z" },
-    { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542, upload-time = "2025-09-01T11:14:06.309Z" },
-    { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244, upload-time = "2025-09-01T11:14:08.152Z" },
-    { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975, upload-time = "2025-09-01T11:14:09.755Z" },
-    { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082, upload-time = "2025-09-01T11:14:11.229Z" },
-    { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397, upload-time = "2025-09-01T11:14:12.924Z" },
-    { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244, upload-time = "2025-09-01T11:14:14.431Z" },
-    { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862, upload-time = "2025-09-01T11:14:16.185Z" },
-    { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578, upload-time = "2025-09-01T11:14:17.638Z" },
-    { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400, upload-time = "2025-09-01T11:14:18.958Z" },
-    { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824, upload-time = "2025-09-01T11:14:20.954Z" },
-    { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233, upload-time = "2025-09-01T11:14:22.454Z" },
-    { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075, upload-time = "2025-09-01T11:14:24.287Z" },
-    { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517, upload-time = "2025-09-01T11:14:25.679Z" },
-    { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893, upload-time = "2025-09-01T11:14:27.1Z" },
-    { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132, upload-time = "2025-09-01T11:14:28.58Z" },
-    { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086, upload-time = "2025-09-01T11:14:30.572Z" },
-    { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383, upload-time = "2025-09-01T11:14:32.046Z" },
-    { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186, upload-time = "2025-09-01T11:14:33.95Z" },
-    { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639, upload-time = "2025-09-01T11:14:35.343Z" },
-    { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552, upload-time = "2025-09-01T11:14:36.929Z" },
-    { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742, upload-time = "2025-09-01T11:14:38.368Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/a9/62/e3664e6ffd7743e1694b244dde70b43a394f6f7fbcacf7014a8ff5197c73/cryptography-46.0.1.tar.gz", hash = "sha256:ed570874e88f213437f5cf758f9ef26cbfc3f336d889b1e592ee11283bb8d1c7", size = 749198, upload-time = "2025-09-17T00:10:35.797Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4c/8c/44ee01267ec01e26e43ebfdae3f120ec2312aa72fa4c0507ebe41a26739f/cryptography-46.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:1cd6d50c1a8b79af1a6f703709d8973845f677c8e97b1268f5ff323d38ce8475", size = 7285044, upload-time = "2025-09-17T00:08:36.807Z" },
+    { url = "https://files.pythonhosted.org/packages/22/59/9ae689a25047e0601adfcb159ec4f83c0b4149fdb5c3030cc94cd218141d/cryptography-46.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ff483716be32690c14636e54a1f6e2e1b7bf8e22ca50b989f88fa1b2d287080", size = 4308182, upload-time = "2025-09-17T00:08:39.388Z" },
+    { url = "https://files.pythonhosted.org/packages/c4/ee/ca6cc9df7118f2fcd142c76b1da0f14340d77518c05b1ebfbbabca6b9e7d/cryptography-46.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9873bf7c1f2a6330bdfe8621e7ce64b725784f9f0c3a6a55c3047af5849f920e", size = 4572393, upload-time = "2025-09-17T00:08:41.663Z" },
+    { url = "https://files.pythonhosted.org/packages/7f/a3/0f5296f63815d8e985922b05c31f77ce44787b3127a67c0b7f70f115c45f/cryptography-46.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0dfb7c88d4462a0cfdd0d87a3c245a7bc3feb59de101f6ff88194f740f72eda6", size = 4308400, upload-time = "2025-09-17T00:08:43.559Z" },
+    { url = "https://files.pythonhosted.org/packages/5d/8c/74fcda3e4e01be1d32775d5b4dd841acaac3c1b8fa4d0774c7ac8d52463d/cryptography-46.0.1-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e22801b61613ebdebf7deb18b507919e107547a1d39a3b57f5f855032dd7cfb8", size = 4015786, upload-time = "2025-09-17T00:08:45.758Z" },
+    { url = "https://files.pythonhosted.org/packages/dc/b8/85d23287baeef273b0834481a3dd55bbed3a53587e3b8d9f0898235b8f91/cryptography-46.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:757af4f6341ce7a1e47c326ca2a81f41d236070217e5fbbad61bbfe299d55d28", size = 4982606, upload-time = "2025-09-17T00:08:47.602Z" },
+    { url = "https://files.pythonhosted.org/packages/e5/d3/de61ad5b52433b389afca0bc70f02a7a1f074651221f599ce368da0fe437/cryptography-46.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f7a24ea78de345cfa7f6a8d3bde8b242c7fac27f2bd78fa23474ca38dfaeeab9", size = 4604234, upload-time = "2025-09-17T00:08:49.879Z" },
+    { url = "https://files.pythonhosted.org/packages/dc/1f/dbd4d6570d84748439237a7478d124ee0134bf166ad129267b7ed8ea6d22/cryptography-46.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e8776dac9e660c22241b6587fae51a67b4b0147daa4d176b172c3ff768ad736", size = 4307669, upload-time = "2025-09-17T00:08:52.321Z" },
+    { url = "https://files.pythonhosted.org/packages/ec/fd/ca0a14ce7f0bfe92fa727aacaf2217eb25eb7e4ed513b14d8e03b26e63ed/cryptography-46.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9f40642a140c0c8649987027867242b801486865277cbabc8c6059ddef16dc8b", size = 4947579, upload-time = "2025-09-17T00:08:54.697Z" },
+    { url = "https://files.pythonhosted.org/packages/89/6b/09c30543bb93401f6f88fce556b3bdbb21e55ae14912c04b7bf355f5f96c/cryptography-46.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:449ef2b321bec7d97ef2c944173275ebdab78f3abdd005400cc409e27cd159ab", size = 4603669, upload-time = "2025-09-17T00:08:57.16Z" },
+    { url = "https://files.pythonhosted.org/packages/23/9a/38cb01cb09ce0adceda9fc627c9cf98eb890fc8d50cacbe79b011df20f8a/cryptography-46.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2dd339ba3345b908fa3141ddba4025568fa6fd398eabce3ef72a29ac2d73ad75", size = 4435828, upload-time = "2025-09-17T00:08:59.606Z" },
+    { url = "https://files.pythonhosted.org/packages/0f/53/435b5c36a78d06ae0bef96d666209b0ecd8f8181bfe4dda46536705df59e/cryptography-46.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7411c910fb2a412053cf33cfad0153ee20d27e256c6c3f14d7d7d1d9fec59fd5", size = 4709553, upload-time = "2025-09-17T00:09:01.832Z" },
+    { url = "https://files.pythonhosted.org/packages/f5/c4/0da6e55595d9b9cd3b6eb5dc22f3a07ded7f116a3ea72629cab595abb804/cryptography-46.0.1-cp311-abi3-win32.whl", hash = "sha256:cbb8e769d4cac884bb28e3ff620ef1001b75588a5c83c9c9f1fdc9afbe7f29b0", size = 3058327, upload-time = "2025-09-17T00:09:03.726Z" },
+    { url = "https://files.pythonhosted.org/packages/95/0f/cd29a35e0d6e78a0ee61793564c8cff0929c38391cb0de27627bdc7525aa/cryptography-46.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:92e8cfe8bd7dd86eac0a677499894862cd5cc2fd74de917daa881d00871ac8e7", size = 3523893, upload-time = "2025-09-17T00:09:06.272Z" },
+    { url = "https://files.pythonhosted.org/packages/f2/dd/eea390f3e78432bc3d2f53952375f8b37cb4d37783e626faa6a51e751719/cryptography-46.0.1-cp311-abi3-win_arm64.whl", hash = "sha256:db5597a4c7353b2e5fb05a8e6cb74b56a4658a2b7bf3cb6b1821ae7e7fd6eaa0", size = 2932145, upload-time = "2025-09-17T00:09:08.568Z" },
+    { url = "https://files.pythonhosted.org/packages/98/e5/fbd632385542a3311915976f88e0dfcf09e62a3fc0aff86fb6762162a24d/cryptography-46.0.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d84c40bdb8674c29fa192373498b6cb1e84f882889d21a471b45d1f868d8d44b", size = 7255677, upload-time = "2025-09-17T00:09:42.407Z" },
+    { url = "https://files.pythonhosted.org/packages/56/3e/13ce6eab9ad6eba1b15a7bd476f005a4c1b3f299f4c2f32b22408b0edccf/cryptography-46.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ed64e5083fa806709e74fc5ea067dfef9090e5b7a2320a49be3c9df3583a2d8", size = 4301110, upload-time = "2025-09-17T00:09:45.614Z" },
+    { url = "https://files.pythonhosted.org/packages/a2/67/65dc233c1ddd688073cf7b136b06ff4b84bf517ba5529607c9d79720fc67/cryptography-46.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:341fb7a26bc9d6093c1b124b9f13acc283d2d51da440b98b55ab3f79f2522ead", size = 4562369, upload-time = "2025-09-17T00:09:47.601Z" },
+    { url = "https://files.pythonhosted.org/packages/17/db/d64ae4c6f4e98c3dac5bf35dd4d103f4c7c345703e43560113e5e8e31b2b/cryptography-46.0.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6ef1488967e729948d424d09c94753d0167ce59afba8d0f6c07a22b629c557b2", size = 4302126, upload-time = "2025-09-17T00:09:49.335Z" },
+    { url = "https://files.pythonhosted.org/packages/3d/19/5f1eea17d4805ebdc2e685b7b02800c4f63f3dd46cfa8d4c18373fea46c8/cryptography-46.0.1-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7823bc7cdf0b747ecfb096d004cc41573c2f5c7e3a29861603a2871b43d3ef32", size = 4009431, upload-time = "2025-09-17T00:09:51.239Z" },
+    { url = "https://files.pythonhosted.org/packages/81/b5/229ba6088fe7abccbfe4c5edb96c7a5ad547fac5fdd0d40aa6ea540b2985/cryptography-46.0.1-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:f736ab8036796f5a119ff8211deda416f8c15ce03776db704a7a4e17381cb2ef", size = 4980739, upload-time = "2025-09-17T00:09:54.181Z" },
+    { url = "https://files.pythonhosted.org/packages/3a/9c/50aa38907b201e74bc43c572f9603fa82b58e831bd13c245613a23cff736/cryptography-46.0.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e46710a240a41d594953012213ea8ca398cd2448fbc5d0f1be8160b5511104a0", size = 4592289, upload-time = "2025-09-17T00:09:56.731Z" },
+    { url = "https://files.pythonhosted.org/packages/5a/33/229858f8a5bb22f82468bb285e9f4c44a31978d5f5830bb4ea1cf8a4e454/cryptography-46.0.1-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:84ef1f145de5aee82ea2447224dc23f065ff4cc5791bb3b506615957a6ba8128", size = 4301815, upload-time = "2025-09-17T00:09:58.548Z" },
+    { url = "https://files.pythonhosted.org/packages/52/cb/b76b2c87fbd6ed4a231884bea3ce073406ba8e2dae9defad910d33cbf408/cryptography-46.0.1-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9394c7d5a7565ac5f7d9ba38b2617448eba384d7b107b262d63890079fad77ca", size = 4943251, upload-time = "2025-09-17T00:10:00.475Z" },
+    { url = "https://files.pythonhosted.org/packages/94/0f/f66125ecf88e4cb5b8017ff43f3a87ede2d064cb54a1c5893f9da9d65093/cryptography-46.0.1-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ed957044e368ed295257ae3d212b95456bd9756df490e1ac4538857f67531fcc", size = 4591247, upload-time = "2025-09-17T00:10:02.874Z" },
+    { url = "https://files.pythonhosted.org/packages/f6/22/9f3134ae436b63b463cfdf0ff506a0570da6873adb4bf8c19b8a5b4bac64/cryptography-46.0.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f7de12fa0eee6234de9a9ce0ffcfa6ce97361db7a50b09b65c63ac58e5f22fc7", size = 4428534, upload-time = "2025-09-17T00:10:04.994Z" },
+    { url = "https://files.pythonhosted.org/packages/89/39/e6042bcb2638650b0005c752c38ea830cbfbcbb1830e4d64d530000aa8dc/cryptography-46.0.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7fab1187b6c6b2f11a326f33b036f7168f5b996aedd0c059f9738915e4e8f53a", size = 4699541, upload-time = "2025-09-17T00:10:06.925Z" },
+    { url = "https://files.pythonhosted.org/packages/68/46/753d457492d15458c7b5a653fc9a84a1c9c7a83af6ebdc94c3fc373ca6e8/cryptography-46.0.1-cp38-abi3-win32.whl", hash = "sha256:45f790934ac1018adeba46a0f7289b2b8fe76ba774a88c7f1922213a56c98bc1", size = 3043779, upload-time = "2025-09-17T00:10:08.951Z" },
+    { url = "https://files.pythonhosted.org/packages/2f/50/b6f3b540c2f6ee712feeb5fa780bb11fad76634e71334718568e7695cb55/cryptography-46.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:7176a5ab56fac98d706921f6416a05e5aff7df0e4b91516f450f8627cda22af3", size = 3517226, upload-time = "2025-09-17T00:10:10.769Z" },
+    { url = "https://files.pythonhosted.org/packages/ff/e8/77d17d00981cdd27cc493e81e1749a0b8bbfb843780dbd841e30d7f50743/cryptography-46.0.1-cp38-abi3-win_arm64.whl", hash = "sha256:efc9e51c3e595267ff84adf56e9b357db89ab2279d7e375ffcaf8f678606f3d9", size = 2923149, upload-time = "2025-09-17T00:10:13.236Z" },
 ]
 
 [[package]]
@@ -365,10 +329,9 @@ wheels = [
 
 [[package]]
 name = "hip-3-pusher"
-version = "0.1.3"
-source = { virtual = "." }
+version = "0.1.4"
+source = { editable = "." }
 dependencies = [
-    { name = "asn1crypto" },
     { name = "boto3" },
     { name = "cryptography" },
     { name = "hyperliquid-python-sdk" },
@@ -376,24 +339,35 @@ dependencies = [
     { name = "opentelemetry-exporter-prometheus" },
     { name = "opentelemetry-sdk" },
     { name = "prometheus-client" },
-    { name = "toml" },
+    { name = "setuptools" },
+    { name = "tenacity" },
     { name = "websockets" },
+    { name = "wheel" },
+]
+
+[package.dev-dependencies]
+dev = [
+    { name = "pytest" },
 ]
 
 [package.metadata]
 requires-dist = [
-    { name = "asn1crypto", specifier = ">=1.5.1" },
-    { name = "boto3", specifier = ">=1.40.31" },
-    { name = "cryptography", specifier = ">=45.0.7" },
-    { name = "hyperliquid-python-sdk", specifier = ">=0.19.0" },
-    { name = "loguru", specifier = ">=0.7.3" },
-    { name = "opentelemetry-exporter-prometheus", specifier = ">=0.58b0" },
-    { name = "opentelemetry-sdk", specifier = ">=1.37.0" },
-    { name = "prometheus-client", specifier = ">=0.23.1" },
-    { name = "toml", specifier = ">=0.10.2" },
-    { name = "websockets", specifier = ">=15.0.1" },
+    { name = "boto3", specifier = "~=1.40.38" },
+    { name = "cryptography", specifier = "~=46.0.1" },
+    { name = "hyperliquid-python-sdk", specifier = "~=0.19.0" },
+    { name = "loguru", specifier = "~=0.7.3" },
+    { name = "opentelemetry-exporter-prometheus", specifier = "~=0.58b0" },
+    { name = "opentelemetry-sdk", specifier = "~=1.37.0" },
+    { name = "prometheus-client", specifier = "~=0.23.1" },
+    { name = "setuptools", specifier = "~=80.9" },
+    { name = "tenacity", specifier = "~=9.1.2" },
+    { name = "websockets", specifier = "~=15.0.1" },
+    { name = "wheel", specifier = "~=0.45.1" },
 ]
 
+[package.metadata.requires-dev]
+dev = [{ name = "pytest", specifier = "~=8.4.2" }]
+
 [[package]]
 name = "hyperliquid-python-sdk"
 version = "0.19.0"
@@ -431,6 +405,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
 ]
 
+[[package]]
+name = "iniconfig"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
+]
+
 [[package]]
 name = "jmespath"
 version = "1.0.1"
@@ -525,6 +508,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/07/90/68152b7465f50285d3ce2481b3aec2f82822e3f52e5152eeeaf516bab841/opentelemetry_semantic_conventions-0.58b0-py3-none-any.whl", hash = "sha256:5564905ab1458b96684db1340232729fce3b5375a06e140e8904c78e4f815b28", size = 207954, upload-time = "2025-09-11T10:28:59.218Z" },
 ]
 
+[[package]]
+name = "packaging"
+version = "25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+]
+
 [[package]]
 name = "parsimonious"
 version = "0.10.0"
@@ -537,6 +529,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/aa/0f/c8b64d9b54ea631fcad4e9e3c8dbe8c11bb32a623be94f22974c88e71eaf/parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f", size = 48427, upload-time = "2022-09-03T17:01:13.814Z" },
 ]
 
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
 [[package]]
 name = "prometheus-client"
 version = "0.23.1"
@@ -628,6 +629,31 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
 ]
 
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "8.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "colorama", marker = "sys_platform == 'win32'" },
+    { name = "iniconfig" },
+    { name = "packaging" },
+    { name = "pluggy" },
+    { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
+]
+
 [[package]]
 name = "python-dateutil"
 version = "2.9.0.post0"
@@ -660,20 +686,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/b5/36/674672f3fdead107565a2499f3007788b878188acec6d42bc141c5366c2c/regex-2025.9.1-cp313-cp313-win32.whl", hash = "sha256:3b9a62107a7441b81ca98261808fed30ae36ba06c8b7ee435308806bd53c1ed8", size = 264508, upload-time = "2025-09-01T22:09:16.193Z" },
     { url = "https://files.pythonhosted.org/packages/83/ad/931134539515eb64ce36c24457a98b83c1b2e2d45adf3254b94df3735a76/regex-2025.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:b38afecc10c177eb34cfae68d669d5161880849ba70c05cbfbe409f08cc939d7", size = 275469, upload-time = "2025-09-01T22:09:17.462Z" },
     { url = "https://files.pythonhosted.org/packages/24/8c/96d34e61c0e4e9248836bf86d69cb224fd222f270fa9045b24e218b65604/regex-2025.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:ec329890ad5e7ed9fc292858554d28d58d56bf62cf964faf0aa57964b21155a0", size = 268586, upload-time = "2025-09-01T22:09:18.948Z" },
-    { url = "https://files.pythonhosted.org/packages/21/b1/453cbea5323b049181ec6344a803777914074b9726c9c5dc76749966d12d/regex-2025.9.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:72fb7a016467d364546f22b5ae86c45680a4e0de6b2a6f67441d22172ff641f1", size = 486111, upload-time = "2025-09-01T22:09:20.734Z" },
-    { url = "https://files.pythonhosted.org/packages/f6/0e/92577f197bd2f7652c5e2857f399936c1876978474ecc5b068c6d8a79c86/regex-2025.9.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c9527fa74eba53f98ad86be2ba003b3ebe97e94b6eb2b916b31b5f055622ef03", size = 289520, upload-time = "2025-09-01T22:09:22.249Z" },
-    { url = "https://files.pythonhosted.org/packages/af/c6/b472398116cca7ea5a6c4d5ccd0fc543f7fd2492cb0c48d2852a11972f73/regex-2025.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c905d925d194c83a63f92422af7544ec188301451b292c8b487f0543726107ca", size = 287215, upload-time = "2025-09-01T22:09:23.657Z" },
-    { url = "https://files.pythonhosted.org/packages/cf/11/f12ecb0cf9ca792a32bb92f758589a84149017467a544f2f6bfb45c0356d/regex-2025.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74df7c74a63adcad314426b1f4ea6054a5ab25d05b0244f0c07ff9ce640fa597", size = 797855, upload-time = "2025-09-01T22:09:25.197Z" },
-    { url = "https://files.pythonhosted.org/packages/46/88/bbb848f719a540fb5997e71310f16f0b33a92c5d4b4d72d4311487fff2a3/regex-2025.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4f6e935e98ea48c7a2e8be44494de337b57a204470e7f9c9c42f912c414cd6f5", size = 863363, upload-time = "2025-09-01T22:09:26.705Z" },
-    { url = "https://files.pythonhosted.org/packages/54/a9/2321eb3e2838f575a78d48e03c1e83ea61bd08b74b7ebbdeca8abc50fc25/regex-2025.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4a62d033cd9ebefc7c5e466731a508dfabee827d80b13f455de68a50d3c2543d", size = 910202, upload-time = "2025-09-01T22:09:28.906Z" },
-    { url = "https://files.pythonhosted.org/packages/33/07/d1d70835d7d11b7e126181f316f7213c4572ecf5c5c97bdbb969fb1f38a2/regex-2025.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef971ebf2b93bdc88d8337238be4dfb851cc97ed6808eb04870ef67589415171", size = 801808, upload-time = "2025-09-01T22:09:30.733Z" },
-    { url = "https://files.pythonhosted.org/packages/13/d1/29e4d1bed514ef2bf3a4ead3cb8bb88ca8af94130239a4e68aa765c35b1c/regex-2025.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d936a1db208bdca0eca1f2bb2c1ba1d8370b226785c1e6db76e32a228ffd0ad5", size = 786824, upload-time = "2025-09-01T22:09:32.61Z" },
-    { url = "https://files.pythonhosted.org/packages/33/27/20d8ccb1bee460faaa851e6e7cc4cfe852a42b70caa1dca22721ba19f02f/regex-2025.9.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7e786d9e4469698fc63815b8de08a89165a0aa851720eb99f5e0ea9d51dd2b6a", size = 857406, upload-time = "2025-09-01T22:09:34.117Z" },
-    { url = "https://files.pythonhosted.org/packages/74/fe/60c6132262dc36430d51e0c46c49927d113d3a38c1aba6a26c7744c84cf3/regex-2025.9.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6b81d7dbc5466ad2c57ce3a0ddb717858fe1a29535c8866f8514d785fdb9fc5b", size = 848593, upload-time = "2025-09-01T22:09:35.598Z" },
-    { url = "https://files.pythonhosted.org/packages/cc/ae/2d4ff915622fabbef1af28387bf71e7f2f4944a348b8460d061e85e29bf0/regex-2025.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cd4890e184a6feb0ef195338a6ce68906a8903a0f2eb7e0ab727dbc0a3156273", size = 787951, upload-time = "2025-09-01T22:09:37.139Z" },
-    { url = "https://files.pythonhosted.org/packages/85/37/dc127703a9e715a284cc2f7dbdd8a9776fd813c85c126eddbcbdd1ca5fec/regex-2025.9.1-cp314-cp314-win32.whl", hash = "sha256:34679a86230e46164c9e0396b56cab13c0505972343880b9e705083cc5b8ec86", size = 269833, upload-time = "2025-09-01T22:09:39.245Z" },
-    { url = "https://files.pythonhosted.org/packages/83/bf/4bed4d3d0570e16771defd5f8f15f7ea2311edcbe91077436d6908956c4a/regex-2025.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:a1196e530a6bfa5f4bde029ac5b0295a6ecfaaffbfffede4bbaf4061d9455b70", size = 278742, upload-time = "2025-09-01T22:09:40.651Z" },
-    { url = "https://files.pythonhosted.org/packages/cf/3e/7d7ac6fd085023312421e0d69dfabdfb28e116e513fadbe9afe710c01893/regex-2025.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:f46d525934871ea772930e997d577d48c6983e50f206ff7b66d4ac5f8941e993", size = 271860, upload-time = "2025-09-01T22:09:42.413Z" },
 ]
 
 [[package]]
@@ -715,6 +727,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/48/f0/ae7ca09223a81a1d890b2557186ea015f6e0502e9b8cb8e1813f1d8cfa4e/s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456", size = 85712, upload-time = "2025-09-09T19:23:30.041Z" },
 ]
 
+[[package]]
+name = "setuptools"
+version = "80.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
+]
+
 [[package]]
 name = "six"
 version = "1.17.0"
@@ -725,12 +746,12 @@ wheels = [
 ]
 
 [[package]]
-name = "toml"
-version = "0.10.2"
+name = "tenacity"
+version = "9.1.2"
 source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
+    { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" },
 ]
 
 [[package]]
@@ -801,6 +822,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
 ]
 
+[[package]]
+name = "wheel"
+version = "0.45.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" },
+]
+
 [[package]]
 name = "win32-setctime"
 version = "1.2.0"

+ 7 - 5
apps/insights/src/services/pyth/get-feeds.ts

@@ -6,11 +6,13 @@ import { priceFeedsSchema } from "../../schemas/pyth/price-feeds-schema";
 const _getFeeds = async (cluster: Cluster) => {
   const unfilteredData = await getPythMetadata(cluster);
   const filtered = unfilteredData.symbols
-    .filter(
-      (symbol) =>
-        unfilteredData.productFromSymbol.get(symbol)?.display_symbol !==
-        undefined,
-    )
+    .filter((symbol) => {
+      const product = unfilteredData.productFromSymbol.get(symbol);
+      const hasDisplaySymbol = product?.display_symbol !== undefined;
+      const hasPriceAccount = product?.price_account !== undefined;
+
+      return hasDisplaySymbol && hasPriceAccount;
+    })
     .map((symbol) => ({
       symbol,
       product: unfilteredData.productFromSymbol.get(symbol),

+ 5 - 1
contract_manager/scripts/deploy_evm_executor_contracts.ts

@@ -120,8 +120,12 @@ export async function main() {
     CACHE_FILE,
   );
 
+  const maskedDeploymentConfig = {
+    ...deploymentConfig,
+    privateKey: deploymentConfig.privateKey ? `<REDACTED>` : undefined,
+  };
   console.log(
-    `Deployment config: ${JSON.stringify(deploymentConfig, null, 2)}\n`,
+    `Deployment config: ${JSON.stringify(maskedDeploymentConfig, null, 2)}\n`,
   );
 
   console.log(`Deploying executor contracts on ${chain.getId()}...`);

+ 14 - 0
contract_manager/store/chains/EvmChains.json

@@ -1328,5 +1328,19 @@
     "rpcUrl": "https://sepolia-rpc.giwa.io",
     "networkId": 91342,
     "type": "EvmChain"
+  },
+  {
+    "id": "itsnotreal2",
+    "mainnet": true,
+    "rpcUrl": "https://rpc.itsnotreal2.lol",
+    "networkId": 5064014,
+    "type": "EvmChain"
+  },
+  {
+    "id": "plasma",
+    "mainnet": true,
+    "rpcUrl": "https://rpc.plasma.to/",
+    "networkId": 9745,
+    "type": "EvmChain"
   }
 ]

+ 6 - 1
contract_manager/store/contracts/EvmExecutorContracts.json

@@ -233,5 +233,10 @@
     "chain": "giwa_testnet",
     "address": "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c",
     "type": "EvmExecutorContract"
+  },
+  {
+    "chain": "itsnotreal2",
+    "address": "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb",
+    "type": "EvmExecutorContract"
   }
-]
+]

+ 5 - 0
contract_manager/store/contracts/EvmLazerContracts.json

@@ -63,5 +63,10 @@
     "chain": "itsnotreal",
     "address": "0xACeA761c27A909d4D3895128EBe6370FDE2dF481",
     "type": "EvmLazerContract"
+  },
+  {
+    "chain": "itsnotreal2",
+    "address": "0xACeA761c27A909d4D3895128EBe6370FDE2dF481",
+    "type": "EvmLazerContract"
   }
 ]

+ 5 - 0
contract_manager/store/contracts/EvmPriceFeedContracts.json

@@ -873,5 +873,10 @@
     "chain": "giwa_testnet",
     "address": "0x2880aB155794e7179c9eE2e38200202908C17B43",
     "type": "EvmPriceFeedContract"
+  },
+  {
+    "chain": "plasma",
+    "address": "0x2880aB155794e7179c9eE2e38200202908C17B43",
+    "type": "EvmPriceFeedContract"
   }
 ]

+ 11 - 1
contract_manager/store/contracts/EvmWormholeContracts.json

@@ -883,5 +883,15 @@
     "chain": "giwa_testnet",
     "address": "0xb27e5ca259702f209a29225d0eDdC131039C9933",
     "type": "EvmWormholeContract"
+  },
+  {
+    "chain": "itsnotreal2",
+    "address": "0x35a58BeeE77a2Ad547FcDed7e8CB1c6e19746b13",
+    "type": "EvmWormholeContract"
+  },
+  {
+    "chain": "plasma",
+    "address": "0xb27e5ca259702f209a29225d0eDdC131039C9933",
+    "type": "EvmWormholeContract"
   }
-]
+]

+ 2 - 0
governance/xc_admin/packages/xc_admin_common/src/chains.ts

@@ -118,6 +118,8 @@ export const RECEIVER_CHAINS = {
   spiderman: 60087,
   zero_gravity: 60088,
   itsnotreal: 60089,
+  plasma: 60090,
+  itsnotreal2: 60091,
 
   // Testnets as a separate chain ids (to use stable data sources and governance for them)
   injective_testnet: 60013,

+ 2 - 2
lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "pyth-lazer-solana-contract"
-version = "0.7.0"
+version = "0.7.1"
 edition = "2021"
 description = "Pyth Lazer Solana contract and SDK."
 license = "Apache-2.0"
@@ -19,7 +19,7 @@ no-log-ix-name = []
 idl-build = ["anchor-lang/idl-build"]
 
 [dependencies]
-pyth-lazer-protocol = { path = "../../../../sdk/rust/protocol", version = "0.15.0" }
+pyth-lazer-protocol = { path = "../../../../sdk/rust/protocol", version = "0.16.0" }
 
 anchor-lang = "0.31.1"
 bytemuck = { version = "1.20.0", features = ["derive"] }

+ 6 - 27
lazer/contracts/sui/sdk/js/examples/fetch-and-verify-update.ts

@@ -1,44 +1,26 @@
 import { SuiClient } from "@mysten/sui/client";
 import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
 import { Transaction } from "@mysten/sui/transactions";
-import type { Request as SubscriptionRequest } from "@pythnetwork/pyth-lazer-sdk";
 import { PythLazerClient } from "@pythnetwork/pyth-lazer-sdk";
 import yargs from "yargs";
 import { hideBin } from "yargs/helpers";
 
 import { addParseAndVerifyLeEcdsaUpdateCall } from "../src/client.js";
 
-async function getOneLeEcdsaUpdate(urls: string[], token: string) {
+async function getOneLeEcdsaUpdate(token: string) {
   const lazer = await PythLazerClient.create({
-    urls,
     token,
-    numConnections: 1,
   });
 
-  const subscription: SubscriptionRequest = {
-    subscriptionId: 1,
-    type: "subscribe",
+  const latestPrice = await lazer.get_latest_price({
     priceFeedIds: [1],
     properties: ["price", "bestBidPrice", "bestAskPrice", "exponent"],
     formats: ["leEcdsa"],
     channel: "fixed_rate@200ms",
-    deliveryFormat: "binary",
     jsonBinaryEncoding: "hex",
-  };
-
-  lazer.subscribe(subscription);
-
-  return new Promise<Uint8Array>((resolve) => {
-    lazer.addMessageListener((event) => {
-      if (event.type === "binary" && event.value.leEcdsa) {
-        const buf = event.value.leEcdsa;
-
-        // For the purposes of this example, we only need one update.
-        lazer.shutdown();
-        resolve(buf);
-      }
-    });
   });
+
+  return latestPrice;
 }
 
 async function main() {
@@ -87,10 +69,7 @@ async function main() {
   const provider = new SuiClient({ url: args.fullnodeUrl });
 
   // Fetch the price update
-  const updateBytes = await getOneLeEcdsaUpdate(
-    args.lazerUrls,
-    args.lazerToken,
-  );
+  const update = await getOneLeEcdsaUpdate(args.lazerToken);
 
   // Build the Sui transaction
   const tx = new Transaction();
@@ -100,7 +79,7 @@ async function main() {
     tx,
     packageId: args.packageId,
     stateObjectId: args.stateObjectId,
-    updateBytes,
+    updateBytes: Buffer.from(update.leEcdsa?.data ?? "", "hex"),
   });
 
   // --- You can add more calls to the transaction that consume the parsed update here ---

+ 1 - 1
lazer/contracts/sui/sdk/js/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/pyth-lazer-sui-js",
-  "version": "0.1.0",
+  "version": "0.1.1",
   "description": "TypeScript SDK for the Pyth Lazer Sui contract",
   "license": "Apache-2.0",
   "type": "module",

+ 2 - 2
lazer/publisher_sdk/rust/Cargo.toml

@@ -1,13 +1,13 @@
 [package]
 name = "pyth-lazer-publisher-sdk"
-version = "0.12.1"
+version = "0.13.0"
 edition = "2021"
 description = "Pyth Lazer Publisher SDK types."
 license = "Apache-2.0"
 repository = "https://github.com/pyth-network/pyth-crosschain"
 
 [dependencies]
-pyth-lazer-protocol = { version = "0.15.1", path = "../../sdk/rust/protocol" }
+pyth-lazer-protocol = { version = "0.16.0", path = "../../sdk/rust/protocol" }
 anyhow = "1.0.98"
 protobuf = "3.7.2"
 serde_json = "1.0.140"

+ 48 - 0
lazer/sdk/js/examples/history.ts

@@ -0,0 +1,48 @@
+/* eslint-disable no-console */
+
+import { displayParsedPrices } from "./util.js";
+import { PythLazerClient } from "../src/index.js";
+
+const client = await PythLazerClient.create({
+  token: "your-token-here",
+  logger: console,
+});
+
+// Example 1: Get latest price for BTC using feed IDs
+console.log("\n=== Example 1: Latest BTC price (requested with feed ID) ===");
+const response1 = await client.get_latest_price({
+  priceFeedIds: [1],
+  properties: ["price", "confidence", "exponent"],
+  formats: [],
+  jsonBinaryEncoding: "hex",
+  parsed: true,
+  channel: "fixed_rate@200ms",
+});
+displayParsedPrices(response1);
+
+// Example 2: Get latest price using symbols
+console.log("\n=== Example 2: Latest ETH price (requested with symbols) ===");
+const response2 = await client.get_latest_price({
+  priceFeedIds: [2],
+  properties: ["price", "confidence", "exponent"],
+  formats: [],
+  parsed: true,
+  channel: "real_time",
+});
+displayParsedPrices(response2);
+
+// Example 3: Get historical price at specific timestamp
+console.log("\n=== Example 3: Historical BTC price at timestamp ===");
+const timestamp = 1_754_348_458_565_000;
+console.log(
+  `Requesting price from timestamp: ${timestamp.toString()} (${new Date(timestamp / 1000).toISOString()})`,
+);
+const response3 = await client.get_price({
+  timestamp: timestamp,
+  priceFeedIds: [1],
+  properties: ["price", "confidence", "exponent"],
+  formats: [],
+  parsed: true,
+  channel: "real_time",
+});
+displayParsedPrices(response3);

+ 0 - 89
lazer/sdk/js/examples/index.ts

@@ -1,89 +0,0 @@
-/* eslint-disable no-console */
-/* eslint-disable @typescript-eslint/no-empty-function */
-
-import { PythLazerClient } from "../src/index.js";
-
-// Ignore debug messages
-console.debug = () => {};
-
-const client = await PythLazerClient.create({
-  urls: [
-    "wss://pyth-lazer-0.dourolabs.app/v1/stream",
-    "wss://pyth-lazer-1.dourolabs.app/v1/stream",
-  ],
-  token: "you-access-token-here", // Replace with your actual access token
-  numConnections: 4, // Optionally specify number of parallel redundant connections to reduce the chance of dropped messages. The connections will round-robin across the provided URLs. Default is 4.
-  logger: console, // Optionally log socket operations (to the console in this case.)
-  onError: (error) => {
-    console.error("WebSocket error:", error);
-  },
-  // Optional configuration for resilient WebSocket connections
-  rwsConfig: {
-    heartbeatTimeoutDurationMs: 5000, // Optional heartbeat timeout duration in milliseconds
-    maxRetryDelayMs: 1000, // Optional maximum retry delay in milliseconds
-    logAfterRetryCount: 10, // Optional log after how many retries
-  },
-});
-
-// Read and process messages from the Lazer stream
-client.addMessageListener((message) => {
-  console.info("got message:", message);
-  switch (message.type) {
-    case "json": {
-      if (message.value.type == "streamUpdated") {
-        console.info(
-          "stream updated for subscription",
-          message.value.subscriptionId,
-          ":",
-          message.value.parsed?.priceFeeds,
-        );
-      }
-      break;
-    }
-    case "binary": {
-      if ("solana" in message.value) {
-        console.info("solana message:", message.value.solana?.toString("hex"));
-      }
-      if ("evm" in message.value) {
-        console.info("evm message:", message.value.evm?.toString("hex"));
-      }
-      break;
-    }
-  }
-});
-
-// Monitor for all connections in the pool being down simultaneously (e.g. if the internet goes down)
-// The connections may still try to reconnect in the background. To shut down the client completely, call shutdown().
-client.addAllConnectionsDownListener(() => {
-  console.error("All connections are down!");
-});
-
-// Create and remove one or more subscriptions on the fly
-client.subscribe({
-  type: "subscribe",
-  subscriptionId: 1,
-  priceFeedIds: [1, 2],
-  properties: ["price"],
-  formats: ["solana"],
-  deliveryFormat: "binary",
-  channel: "fixed_rate@200ms",
-  parsed: false,
-  jsonBinaryEncoding: "base64",
-});
-client.subscribe({
-  type: "subscribe",
-  subscriptionId: 2,
-  priceFeedIds: [1, 2, 3, 4, 5],
-  properties: ["price", "exponent", "publisherCount", "confidence"],
-  formats: ["evm"],
-  deliveryFormat: "json",
-  channel: "fixed_rate@200ms",
-  parsed: true,
-  jsonBinaryEncoding: "hex",
-});
-
-await new Promise((resolve) => setTimeout(resolve, 10_000));
-
-client.unsubscribe(1);
-client.unsubscribe(2);
-client.shutdown();

+ 125 - 0
lazer/sdk/js/examples/streaming.ts

@@ -0,0 +1,125 @@
+/* eslint-disable no-console */
+/* eslint-disable @typescript-eslint/no-empty-function */
+/* eslint-disable unicorn/prefer-top-level-await */
+
+import { renderFeeds, refreshFeedDisplay } from "./util.js";
+import type { JsonUpdate } from "../src/index.js";
+import { PythLazerClient } from "../src/index.js";
+
+// Ignore debug messages
+console.debug = () => {};
+
+// Store feed data for in-place updates
+const feedData = new Map<
+  string,
+  {
+    priceFeedId: number;
+    price: number;
+    confidence: number | undefined;
+    exponent: number;
+    lastUpdate: Date;
+  }
+>();
+const symbolsMap = new Map<number, string>();
+
+const client = await PythLazerClient.create({
+  token: "your-token-here", // Replace with your actual access token
+  logger: console, // Optionally log operations (to the console in this case.)
+  webSocketPoolConfig: {
+    numConnections: 4, // Optionally specify number of parallel redundant connections to reduce the chance of dropped messages. The connections will round-robin across the provided URLs. Default is 4.
+    onError: (error) => {
+      console.error("WebSocket error:", error);
+    },
+    // Optional configuration for resilient WebSocket connections
+    rwsConfig: {
+      heartbeatTimeoutDurationMs: 5000, // Optional heartbeat timeout duration in milliseconds
+      maxRetryDelayMs: 1000, // Optional maximum retry delay in milliseconds
+      logAfterRetryCount: 10, // Optional log after how many retries
+    },
+  },
+});
+
+// Fetch current map of price feeds
+void client.get_symbols().then((symbols) => {
+  for (const symbol of symbols) {
+    symbolsMap.set(symbol.pyth_lazer_id, symbol.symbol);
+  }
+});
+
+// Add a listener to read and display messages from the Lazer stream
+client.addMessageListener((message) => {
+  switch (message.type) {
+    case "json": {
+      if (message.value.type == "streamUpdated") {
+        refreshFeedDisplay(message.value as JsonUpdate, feedData, symbolsMap);
+      }
+      break;
+    }
+    case "binary": {
+      // Print out the binary hex messages if you want:
+      // if ("solana" in message.value) {
+      //   console.info("solana message:", message.value.solana?.toString("hex"));
+      // }
+      // if ("evm" in message.value) {
+      //   console.info("evm message:", message.value.evm?.toString("hex"));
+      // }
+      break;
+    }
+  }
+});
+
+// Monitor for all connections in the pool being down simultaneously (e.g. if the internet goes down)
+// The connections may still try to reconnect in the background. To shut down the client completely, call shutdown().
+client.addAllConnectionsDownListener(() => {
+  console.error("All connections are down!");
+});
+
+renderFeeds(feedData, symbolsMap);
+
+// Create and remove one or more subscriptions on the fly
+client.subscribe({
+  type: "subscribe",
+  subscriptionId: 1,
+  priceFeedIds: [1, 2],
+  properties: ["price"],
+  formats: ["solana"],
+  deliveryFormat: "binary",
+  channel: "fixed_rate@200ms",
+  parsed: false,
+  jsonBinaryEncoding: "base64",
+});
+client.subscribe({
+  type: "subscribe",
+  subscriptionId: 2,
+  priceFeedIds: [1, 2, 3, 4, 5],
+  properties: ["price", "exponent", "publisherCount", "confidence"],
+  formats: ["evm"],
+  deliveryFormat: "json",
+  channel: "fixed_rate@50ms",
+  parsed: true,
+  jsonBinaryEncoding: "hex",
+});
+client.subscribe({
+  type: "subscribe",
+  subscriptionId: 3,
+  priceFeedIds: [1],
+  properties: ["price", "confidence"],
+  formats: ["solana"],
+  deliveryFormat: "json",
+  channel: "real_time",
+  parsed: true,
+  jsonBinaryEncoding: "hex",
+});
+
+await new Promise((resolve) => setTimeout(resolve, 30_000));
+
+client.unsubscribe(1);
+client.unsubscribe(2);
+client.unsubscribe(3);
+
+// Clear screen and move cursor to top
+process.stdout.write("\u001B[2J\u001B[H");
+console.log("🛑 Shutting down Pyth Lazer demo after 30 seconds...");
+console.log("👋 Goodbye!");
+
+client.shutdown();

+ 29 - 0
lazer/sdk/js/examples/symbols.ts

@@ -0,0 +1,29 @@
+/* eslint-disable no-console */
+
+import { PythLazerClient } from "../src/index.js";
+
+const client = await PythLazerClient.create({
+  token: "your-token-here",
+  logger: console,
+});
+
+// Example 1: Get latest price for BTC using feed IDs
+console.log("\n=== Example 1: Search feeds by name/symbol ===");
+const response1 = await client.get_symbols({ query: "BTC" });
+console.log(response1);
+
+// Example 2: Get latest price using symbols
+console.log("\n=== Example 2: Get feeds by asset type ===");
+const response2 = await client.get_symbols({
+  asset_type: "equity",
+});
+console.log(response2);
+
+// Example 3: Get feeds by asset type and query
+console.log("\n=== Example 3: Get feeds by asset type and name/symbol ===");
+const response3 = await client.get_symbols({
+  asset_type: "equity",
+  query: "AAPL",
+});
+
+console.log(response3);

+ 131 - 0
lazer/sdk/js/examples/util.ts

@@ -0,0 +1,131 @@
+/* eslint-disable no-console */
+
+import type { JsonUpdate } from "../src/index.js";
+
+// Helper function to render all feeds in place
+export function renderFeeds(
+  feedData: Map<
+    string,
+    {
+      priceFeedId: number;
+      price: number;
+      confidence: number | undefined;
+      exponent: number;
+      lastUpdate: Date;
+    }
+  >,
+  symbolsMap: Map<number, string>,
+) {
+  // Clear screen and move cursor to top
+  process.stdout.write("\u001B[2J\u001B[H");
+
+  if (feedData.size === 0) {
+    console.log("Waiting for price feed data...\n");
+    return;
+  }
+
+  console.log("🔴 Live Lazer Price Feeds\n");
+  console.log("━".repeat(80));
+
+  // Sort feeds by ID for consistent display order
+  const sortedFeeds = [...feedData.values()].sort((a, b) => {
+    const aId = String(a.priceFeedId);
+    const bId = String(b.priceFeedId);
+    return aId.localeCompare(bId);
+  });
+
+  for (const [index, feed] of sortedFeeds.entries()) {
+    const readablePrice = feed.price * Math.pow(10, feed.exponent);
+    const readableConfidence =
+      feed.confidence === undefined
+        ? undefined
+        : feed.confidence * Math.pow(10, feed.exponent);
+    const timeAgo = Math.round(Date.now() - feed.lastUpdate.getTime());
+
+    const symbolName = symbolsMap.get(Number(feed.priceFeedId));
+    const displayName = symbolName
+      ? `Feed ID: ${feed.priceFeedId.toString()} (${symbolName})`
+      : `Feed ID: ${feed.priceFeedId.toString()}`;
+
+    console.log(`\u001B[36m${(index + 1).toString()}. ${displayName}\u001B[0m`);
+    console.log(
+      `   💰 Price: \u001B[32m$${readablePrice.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 8 })}\u001B[0m`,
+    );
+
+    if (readableConfidence !== undefined) {
+      console.log(
+        `   📊 Confidence: \u001B[33m±$${readableConfidence.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 8 })}\u001B[0m`,
+      );
+    }
+
+    console.log(
+      `   ⏰ Updated: \u001B[90m${timeAgo.toString()}ms ago\u001B[0m`,
+    );
+    console.log("");
+  }
+
+  console.log("━".repeat(80));
+  console.log(
+    `\u001B[90mLast refresh: ${new Date().toLocaleTimeString()}\u001B[0m`,
+  );
+}
+
+// Helper function to update price feed data and refresh display
+export function refreshFeedDisplay(
+  response: JsonUpdate,
+  feedData: Map<
+    string,
+    {
+      priceFeedId: number;
+      price: number;
+      confidence: number | undefined;
+      exponent: number;
+      lastUpdate: Date;
+    }
+  >,
+  symbolsMap: Map<number, string>,
+) {
+  if (response.parsed?.priceFeeds) {
+    for (const feed of response.parsed.priceFeeds) {
+      if (feed.price && feed.exponent !== undefined) {
+        const readableConfidence = feed.confidence
+          ? Number(feed.confidence)
+          : undefined;
+
+        feedData.set(feed.priceFeedId.toString(), {
+          priceFeedId: feed.priceFeedId,
+          price: Number(feed.price),
+          confidence: readableConfidence,
+          exponent: feed.exponent,
+          lastUpdate: new Date(),
+        });
+      }
+    }
+
+    renderFeeds(feedData, symbolsMap);
+  }
+}
+
+// Helper function to calculate human-friendly price values
+export function displayParsedPrices(response: JsonUpdate) {
+  if (response.parsed?.priceFeeds) {
+    for (const [index, feed] of response.parsed.priceFeeds.entries()) {
+      if (feed.price && feed.exponent !== undefined) {
+        const readablePrice = Number(feed.price) * Math.pow(10, feed.exponent);
+        const readableConfidence = feed.confidence
+          ? Number(feed.confidence) * Math.pow(10, feed.exponent)
+          : undefined;
+
+        console.log(`Feed ${(index + 1).toString()}:`);
+        console.log(
+          `\tPrice: $${readablePrice.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 8 })}`,
+        );
+        if (readableConfidence !== undefined) {
+          console.log(
+            `\tConfidence: ±$${readableConfidence.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 8 })}`,
+          );
+        }
+      }
+    }
+  }
+}

+ 5 - 2
lazer/sdk/js/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/pyth-lazer-sdk",
-  "version": "2.0.0",
+  "version": "3.0.1",
   "description": "Pyth Lazer SDK",
   "publishConfig": {
     "access": "public"
@@ -28,7 +28,9 @@
     "test:types": "tsc",
     "test:format": "prettier --check .",
     "fix:format": "prettier --write .",
-    "example": "node --loader ts-node/esm examples/index.js",
+    "example:streaming": "node --loader ts-node/esm examples/streaming.js",
+    "example:history": "node --loader ts-node/esm examples/history.js",
+    "example:symbols": "node --loader ts-node/esm examples/symbols.js",
     "doc": "typedoc --out docs/typedoc src"
   },
   "devDependencies": {
@@ -59,6 +61,7 @@
   "license": "Apache-2.0",
   "dependencies": {
     "@isaacs/ttlcache": "^1.4.1",
+    "cross-fetch": "^4.0.0",
     "isomorphic-ws": "^5.0.0",
     "ts-log": "^2.2.7",
     "ws": "^8.18.0"

+ 200 - 15
lazer/sdk/js/src/client.ts

@@ -1,6 +1,22 @@
+import fetch from "cross-fetch";
 import WebSocket from "isomorphic-ws";
+import type { Logger } from "ts-log";
+import { dummyLogger } from "ts-log";
 
-import type { ParsedPayload, Request, Response } from "./protocol.js";
+import {
+  DEFAULT_METADATA_SERVICE_URL,
+  DEFAULT_PRICE_SERVICE_URL,
+} from "./constants.js";
+import type {
+  ParsedPayload,
+  Request,
+  Response,
+  SymbolResponse,
+  SymbolsQueryParams,
+  LatestPriceRequest,
+  PriceRequest,
+  JsonUpdate,
+} from "./protocol.js";
 import { BINARY_UPDATE_FORMAT_MAGIC_LE, FORMAT_MAGICS_LE } from "./protocol.js";
 import type { WebSocketPoolConfig } from "./socket/websocket-pool.js";
 import { WebSocketPool } from "./socket/websocket-pool.js";
@@ -24,19 +40,69 @@ const UINT16_NUM_BYTES = 2;
 const UINT32_NUM_BYTES = 4;
 const UINT64_NUM_BYTES = 8;
 
+export type LazerClientConfig = {
+  token: string;
+  metadataServiceUrl?: string;
+  priceServiceUrl?: string;
+  logger?: Logger;
+  webSocketPoolConfig?: WebSocketPoolConfig;
+};
+
 export class PythLazerClient {
-  private constructor(private readonly wsp: WebSocketPool) {}
+  private constructor(
+    private readonly token: string,
+    private readonly metadataServiceUrl: string,
+    private readonly priceServiceUrl: string,
+    private readonly logger: Logger,
+    private readonly wsp?: WebSocketPool,
+  ) {}
+
+  /**
+   * Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
+   * @throws Error if WebSocket pool is not configured
+   * @returns The WebSocket pool
+   */
+  private getWebSocketPool(): WebSocketPool {
+    if (!this.wsp) {
+      throw new Error(
+        "WebSocket pool is not available. Make sure to provide webSocketPoolConfig when creating the client.",
+      );
+    }
+    return this.wsp;
+  }
 
   /**
    * Creates a new PythLazerClient instance.
-   * @param urls - List of WebSocket URLs of the Pyth Lazer service
-   * @param token - The access token for authentication
-   * @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream. The connections will round-robin across the provided URLs.
-   * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
+   * @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
    */
-  static async create(config: WebSocketPoolConfig): Promise<PythLazerClient> {
-    const wsp = await WebSocketPool.create(config);
-    return new PythLazerClient(wsp);
+  static async create(config: LazerClientConfig): Promise<PythLazerClient> {
+    const token = config.token;
+
+    // Collect and remove trailing slash from URLs
+    const metadataServiceUrl = (
+      config.metadataServiceUrl ?? DEFAULT_METADATA_SERVICE_URL
+    ).replace(/\/+$/, "");
+    const priceServiceUrl = (
+      config.priceServiceUrl ?? DEFAULT_PRICE_SERVICE_URL
+    ).replace(/\/+$/, "");
+    const logger = config.logger ?? dummyLogger;
+
+    // If webSocketPoolConfig is provided, create a WebSocket pool and block until at least one connection is established.
+    let wsp: WebSocketPool | undefined;
+    if (config.webSocketPoolConfig) {
+      wsp = await WebSocketPool.create(
+        config.webSocketPoolConfig,
+        token,
+        logger,
+      );
+    }
+    return new PythLazerClient(
+      token,
+      metadataServiceUrl,
+      priceServiceUrl,
+      logger,
+      wsp,
+    );
   }
 
   /**
@@ -46,7 +112,8 @@ export class PythLazerClient {
    * or a binary response containing EVM, Solana, or parsed payload data.
    */
   addMessageListener(handler: (event: JsonOrBinaryResponse) => void) {
-    this.wsp.addMessageListener((data: WebSocket.Data) => {
+    const wsp = this.getWebSocketPool();
+    wsp.addMessageListener((data: WebSocket.Data) => {
       if (typeof data == "string") {
         handler({
           type: "json",
@@ -97,18 +164,21 @@ export class PythLazerClient {
   }
 
   subscribe(request: Request) {
+    const wsp = this.getWebSocketPool();
     if (request.type !== "subscribe") {
       throw new Error("Request must be a subscribe request");
     }
-    this.wsp.addSubscription(request);
+    wsp.addSubscription(request);
   }
 
   unsubscribe(subscriptionId: number) {
-    this.wsp.removeSubscription(subscriptionId);
+    const wsp = this.getWebSocketPool();
+    wsp.removeSubscription(subscriptionId);
   }
 
   send(request: Request) {
-    this.wsp.sendRequest(request);
+    const wsp = this.getWebSocketPool();
+    wsp.sendRequest(request);
   }
 
   /**
@@ -117,10 +187,125 @@ export class PythLazerClient {
    * @param handler - Function to be called when all connections are down
    */
   addAllConnectionsDownListener(handler: () => void): void {
-    this.wsp.addAllConnectionsDownListener(handler);
+    const wsp = this.getWebSocketPool();
+    wsp.addAllConnectionsDownListener(handler);
   }
 
   shutdown(): void {
-    this.wsp.shutdown();
+    const wsp = this.getWebSocketPool();
+    wsp.shutdown();
+  }
+
+  /**
+   * Private helper method to make authenticated HTTP requests with Bearer token
+   * @param url - The URL to fetch
+   * @param options - Additional fetch options
+   * @returns Promise resolving to the fetch Response
+   */
+  private async authenticatedFetch(
+    url: string,
+    options: RequestInit = {},
+  ): Promise<globalThis.Response> {
+    const headers = {
+      Authorization: `Bearer ${this.token}`,
+      ...(options.headers as Record<string, string>),
+    };
+
+    return fetch(url, {
+      ...options,
+      headers,
+    });
+  }
+
+  /**
+   * Queries the symbols endpoint to get available price feed symbols.
+   * @param params - Optional query parameters to filter symbols
+   * @returns Promise resolving to array of symbol information
+   */
+  async get_symbols(params?: SymbolsQueryParams): Promise<SymbolResponse[]> {
+    const url = new URL(`${this.metadataServiceUrl}/v1/symbols`);
+
+    if (params?.query) {
+      url.searchParams.set("query", params.query);
+    }
+    if (params?.asset_type) {
+      url.searchParams.set("asset_type", params.asset_type);
+    }
+
+    try {
+      const response = await this.authenticatedFetch(url.toString());
+      if (!response.ok) {
+        throw new Error(
+          `HTTP error! status: ${String(response.status)} - ${await response.text()}`,
+        );
+      }
+      return (await response.json()) as SymbolResponse[];
+    } catch (error) {
+      throw new Error(
+        `Failed to fetch symbols: ${error instanceof Error ? error.message : String(error)}`,
+      );
+    }
+  }
+
+  /**
+   * Queries the latest price endpoint to get current price data.
+   * @param params - Parameters for the latest price request
+   * @returns Promise resolving to JsonUpdate with current price data
+   */
+  async get_latest_price(params: LatestPriceRequest): Promise<JsonUpdate> {
+    const url = `${this.priceServiceUrl}/v1/latest_price`;
+
+    try {
+      const body = JSON.stringify(params);
+      this.logger.debug("get_latest_price", { url, body });
+      const response = await this.authenticatedFetch(url, {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+        },
+        body: body,
+      });
+      if (!response.ok) {
+        throw new Error(
+          `HTTP error! status: ${String(response.status)} - ${await response.text()}`,
+        );
+      }
+      return (await response.json()) as JsonUpdate;
+    } catch (error) {
+      throw new Error(
+        `Failed to fetch latest price: ${error instanceof Error ? error.message : String(error)}`,
+      );
+    }
+  }
+
+  /**
+   * Queries the price endpoint to get historical price data at a specific timestamp.
+   * @param params - Parameters for the price request including timestamp
+   * @returns Promise resolving to JsonUpdate with price data at the specified time
+   */
+  async get_price(params: PriceRequest): Promise<JsonUpdate> {
+    const url = `${this.priceServiceUrl}/v1/price`;
+
+    try {
+      const body = JSON.stringify(params);
+      this.logger.debug("get_price", { url, body });
+      const response = await this.authenticatedFetch(url, {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+        },
+        body: body,
+      });
+      if (!response.ok) {
+        throw new Error(
+          `HTTP error! status: ${String(response.status)} - ${await response.text()}`,
+        );
+      }
+      return (await response.json()) as JsonUpdate;
+    } catch (error) {
+      throw new Error(
+        `Failed to fetch price: ${error instanceof Error ? error.message : String(error)}`,
+      );
+    }
   }
 }

+ 7 - 0
lazer/sdk/js/src/constants.ts

@@ -2,3 +2,10 @@ export const SOLANA_LAZER_PROGRAM_ID =
   "pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt";
 export const SOLANA_LAZER_STORAGE_ID =
   "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL";
+export const DEFAULT_METADATA_SERVICE_URL =
+  "https://history.pyth-lazer.dourolabs.app/history";
+export const DEFAULT_PRICE_SERVICE_URL = "https://pyth-lazer-0.dourolabs.app";
+export const DEFAULT_STREAM_SERVICE_0_URL =
+  "wss://pyth-lazer-0.dourolabs.app/v1/stream";
+export const DEFAULT_STREAM_SERVICE_1_URL =
+  "wss://pyth-lazer-1.dourolabs.app/v1/stream";

+ 61 - 0
lazer/sdk/js/src/protocol.ts

@@ -50,6 +50,7 @@ export type JsonBinaryData = {
 
 export type InvalidFeedSubscriptionDetails = {
   unknownIds: number[];
+  unknownSymbols: string[];
   unsupportedChannels: number[];
   unstable: number[];
 };
@@ -97,3 +98,63 @@ export const FORMAT_MAGICS_LE = {
   LE_ECDSA: 1_296_547_300,
   LE_UNSIGNED: 1_499_680_012,
 };
+
+export type AssetType =
+  | "crypto"
+  | "fx"
+  | "equity"
+  | "metal"
+  | "rates"
+  | "nav"
+  | "commodity"
+  | "funding-rate";
+
+export type SymbolsQueryParams = {
+  query?: string;
+  asset_type?: AssetType;
+};
+
+export type SymbolResponse = {
+  pyth_lazer_id: number;
+  name: string;
+  symbol: string;
+  description: string;
+  asset_type: string;
+  exponent: number;
+  min_publishers: number;
+  min_channel: string;
+  state: string;
+  schedule: string;
+  cmc_id?: number | null;
+  hermes_id?: string | null;
+  interval?: string | null;
+};
+
+export type LatestPriceRequest = {
+  priceFeedIds?: number[];
+  symbols?: string[];
+  properties: PriceFeedProperty[];
+  formats: Format[];
+  jsonBinaryEncoding?: JsonBinaryEncoding;
+  parsed?: boolean;
+  channel: Channel;
+};
+
+export type PriceRequest = {
+  timestamp: number;
+  priceFeedIds?: number[];
+  symbols?: string[];
+  properties: PriceFeedProperty[];
+  formats: Format[];
+  jsonBinaryEncoding?: JsonBinaryEncoding;
+  parsed?: boolean;
+  channel: Channel;
+};
+
+export type JsonUpdate = {
+  parsed?: ParsedPayload;
+  evm?: JsonBinaryData;
+  solana?: JsonBinaryData;
+  leEcdsa?: JsonBinaryData;
+  leUnsigned?: JsonBinaryData;
+};

+ 19 - 13
lazer/sdk/js/src/socket/websocket-pool.ts

@@ -7,14 +7,16 @@ import { dummyLogger } from "ts-log";
 import type { Request, Response } from "../protocol.js";
 import type { ResilientWebSocketConfig } from "./resilient-websocket.js";
 import { ResilientWebSocket } from "./resilient-websocket.js";
+import {
+  DEFAULT_STREAM_SERVICE_0_URL,
+  DEFAULT_STREAM_SERVICE_1_URL,
+} from "../constants.js";
 
 const DEFAULT_NUM_CONNECTIONS = 4;
 
 export type WebSocketPoolConfig = {
-  urls: string[];
-  token: string;
+  urls?: string[];
   numConnections?: number;
-  logger?: Logger;
   rwsConfig?: Omit<ResilientWebSocketConfig, "logger" | "endpoint">;
   onError?: (error: ErrorEvent) => void;
 };
@@ -49,31 +51,35 @@ export class WebSocketPool {
    * @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
    * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
    */
-  static async create(config: WebSocketPoolConfig): Promise<WebSocketPool> {
-    if (config.urls.length === 0) {
-      throw new Error("No URLs provided");
-    }
-
-    const logger = config.logger ?? dummyLogger;
-    const pool = new WebSocketPool(logger);
+  static async create(
+    config: WebSocketPoolConfig,
+    token: string,
+    logger?: Logger,
+  ): Promise<WebSocketPool> {
+    const urls = config.urls ?? [
+      DEFAULT_STREAM_SERVICE_0_URL,
+      DEFAULT_STREAM_SERVICE_1_URL,
+    ];
+    const log = logger ?? dummyLogger;
+    const pool = new WebSocketPool(log);
     const numConnections = config.numConnections ?? DEFAULT_NUM_CONNECTIONS;
 
     for (let i = 0; i < numConnections; i++) {
-      const url = config.urls[i % config.urls.length];
+      const url = urls[i % urls.length];
       if (!url) {
         throw new Error(`URLs must not be null or empty`);
       }
       const wsOptions = {
         ...config.rwsConfig?.wsOptions,
         headers: {
-          Authorization: `Bearer ${config.token}`,
+          Authorization: `Bearer ${token}`,
         },
       };
       const rws = new ResilientWebSocket({
         ...config.rwsConfig,
         endpoint: url,
         wsOptions,
-        logger,
+        logger: log,
       });
 
       // If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish

+ 2 - 2
lazer/sdk/rust/client/Cargo.toml

@@ -1,12 +1,12 @@
 [package]
 name = "pyth-lazer-client"
-version = "8.0.1"
+version = "8.2.0"
 edition = "2021"
 description = "A Rust client for Pyth Lazer"
 license = "Apache-2.0"
 
 [dependencies]
-pyth-lazer-protocol = { path = "../protocol", version = "0.15.0" }
+pyth-lazer-protocol = { path = "../protocol", version = "0.16.0" }
 tokio = { version = "1", features = ["full"] }
 tokio-tungstenite = { version = "0.20", features = ["native-tls"] }
 futures-util = "0.3"

+ 1 - 1
lazer/sdk/rust/protocol/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "pyth-lazer-protocol"
-version = "0.15.1"
+version = "0.16.0"
 edition = "2021"
 description = "Pyth Lazer SDK - protocol types."
 license = "Apache-2.0"

+ 160 - 36
lazer/sdk/rust/protocol/src/api.rs

@@ -16,7 +16,8 @@ use crate::{
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-pub struct LatestPriceRequest {
+pub struct LatestPriceRequestRepr {
+    // Either price feed ids or symbols must be specified.
     pub price_feed_ids: Option<Vec<PriceFeedId>>,
     pub symbols: Option<Vec<String>>,
     pub properties: Vec<PriceFeedProperty>,
@@ -32,9 +33,55 @@ pub struct LatestPriceRequest {
     pub channel: Channel,
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct LatestPriceRequest(LatestPriceRequestRepr);
+
+impl<'de> Deserialize<'de> for LatestPriceRequest {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let value = LatestPriceRequestRepr::deserialize(deserializer)?;
+        Self::new(value).map_err(Error::custom)
+    }
+}
+
+impl LatestPriceRequest {
+    pub fn new(value: LatestPriceRequestRepr) -> Result<Self, &'static str> {
+        validate_price_feed_ids_or_symbols(&value.price_feed_ids, &value.symbols)?;
+        validate_optional_nonempty_vec_has_unique_elements(
+            &value.price_feed_ids,
+            "no price feed ids specified",
+            "duplicate price feed ids specified",
+        )?;
+        validate_optional_nonempty_vec_has_unique_elements(
+            &value.symbols,
+            "no symbols specified",
+            "duplicate symbols specified",
+        )?;
+        validate_formats(&value.formats)?;
+        validate_properties(&value.properties)?;
+        Ok(Self(value))
+    }
+}
+
+impl Deref for LatestPriceRequest {
+    type Target = LatestPriceRequestRepr;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+impl DerefMut for LatestPriceRequest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
-pub struct PriceRequest {
+pub struct PriceRequestRepr {
     pub timestamp: TimestampUs,
     // Either price feed ids or symbols must be specified.
     pub price_feed_ids: Option<Vec<PriceFeedId>>,
@@ -50,6 +97,52 @@ pub struct PriceRequest {
     pub channel: Channel,
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PriceRequest(PriceRequestRepr);
+
+impl<'de> Deserialize<'de> for PriceRequest {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let value = PriceRequestRepr::deserialize(deserializer)?;
+        Self::new(value).map_err(Error::custom)
+    }
+}
+
+impl PriceRequest {
+    pub fn new(value: PriceRequestRepr) -> Result<Self, &'static str> {
+        validate_price_feed_ids_or_symbols(&value.price_feed_ids, &value.symbols)?;
+        validate_optional_nonempty_vec_has_unique_elements(
+            &value.price_feed_ids,
+            "no price feed ids specified",
+            "duplicate price feed ids specified",
+        )?;
+        validate_optional_nonempty_vec_has_unique_elements(
+            &value.symbols,
+            "no symbols specified",
+            "duplicate symbols specified",
+        )?;
+        validate_formats(&value.formats)?;
+        validate_properties(&value.properties)?;
+        Ok(Self(value))
+    }
+}
+
+impl Deref for PriceRequest {
+    type Target = PriceRequestRepr;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+impl DerefMut for PriceRequest {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct ReducePriceRequest {
@@ -144,6 +237,7 @@ impl Channel {
             Channel::FixedRate(fixed_rate) => match fixed_rate.duration().as_millis() {
                 50 => ChannelId::FIXED_RATE_50,
                 200 => ChannelId::FIXED_RATE_200,
+                1000 => ChannelId::FIXED_RATE_1000,
                 _ => panic!("unknown channel: {self:?}"),
             },
             Channel::RealTime => ChannelId::REAL_TIME,
@@ -221,40 +315,19 @@ impl<'de> Deserialize<'de> for SubscriptionParams {
 
 impl SubscriptionParams {
     pub fn new(value: SubscriptionParamsRepr) -> Result<Self, &'static str> {
-        if value.price_feed_ids.is_none() && value.symbols.is_none() {
-            return Err("either price feed ids or symbols must be specified");
-        }
-        if value.price_feed_ids.is_some() && value.symbols.is_some() {
-            return Err("either price feed ids or symbols must be specified, not both");
-        }
-
-        if let Some(ref ids) = value.price_feed_ids {
-            if ids.is_empty() {
-                return Err("no price feed ids specified");
-            }
-            if !ids.iter().all_unique() {
-                return Err("duplicate price feed ids specified");
-            }
-        }
-
-        if let Some(ref symbols) = value.symbols {
-            if symbols.is_empty() {
-                return Err("no symbols specified");
-            }
-            if !symbols.iter().all_unique() {
-                return Err("duplicate symbols specified");
-            }
-        }
-
-        if !value.formats.iter().all_unique() {
-            return Err("duplicate formats or chains specified");
-        }
-        if value.properties.is_empty() {
-            return Err("no properties specified");
-        }
-        if !value.properties.iter().all_unique() {
-            return Err("duplicate properties specified");
-        }
+        validate_price_feed_ids_or_symbols(&value.price_feed_ids, &value.symbols)?;
+        validate_optional_nonempty_vec_has_unique_elements(
+            &value.price_feed_ids,
+            "no price feed ids specified",
+            "duplicate price feed ids specified",
+        )?;
+        validate_optional_nonempty_vec_has_unique_elements(
+            &value.symbols,
+            "no symbols specified",
+            "duplicate symbols specified",
+        )?;
+        validate_formats(&value.formats)?;
+        validate_properties(&value.properties)?;
         Ok(Self(value))
     }
 }
@@ -467,6 +540,7 @@ pub struct SubscribedResponse {
 #[serde(rename_all = "camelCase")]
 pub struct InvalidFeedSubscriptionDetails {
     pub unknown_ids: Vec<PriceFeedId>,
+    pub unknown_symbols: Vec<String>,
     pub unsupported_channels: Vec<PriceFeedId>,
     pub unstable: Vec<PriceFeedId>,
 }
@@ -511,3 +585,53 @@ pub struct StreamUpdatedResponse {
     #[serde(flatten)]
     pub payload: JsonUpdate,
 }
+
+// Common validation functions
+fn validate_price_feed_ids_or_symbols(
+    price_feed_ids: &Option<Vec<PriceFeedId>>,
+    symbols: &Option<Vec<String>>,
+) -> Result<(), &'static str> {
+    if price_feed_ids.is_none() && symbols.is_none() {
+        return Err("either price feed ids or symbols must be specified");
+    }
+    if price_feed_ids.is_some() && symbols.is_some() {
+        return Err("either price feed ids or symbols must be specified, not both");
+    }
+    Ok(())
+}
+
+fn validate_optional_nonempty_vec_has_unique_elements<T>(
+    vec: &Option<Vec<T>>,
+    empty_msg: &'static str,
+    duplicate_msg: &'static str,
+) -> Result<(), &'static str>
+where
+    T: Eq + std::hash::Hash,
+{
+    if let Some(ref items) = vec {
+        if items.is_empty() {
+            return Err(empty_msg);
+        }
+        if !items.iter().all_unique() {
+            return Err(duplicate_msg);
+        }
+    }
+    Ok(())
+}
+
+fn validate_properties(properties: &[PriceFeedProperty]) -> Result<(), &'static str> {
+    if properties.is_empty() {
+        return Err("no properties specified");
+    }
+    if !properties.iter().all_unique() {
+        return Err("duplicate properties specified");
+    }
+    Ok(())
+}
+
+fn validate_formats(formats: &[Format]) -> Result<(), &'static str> {
+    if !formats.iter().all_unique() {
+        return Err("duplicate formats or chains specified");
+    }
+    Ok(())
+}

+ 1 - 0
lazer/sdk/rust/protocol/src/lib.rs

@@ -52,6 +52,7 @@ impl ChannelId {
     pub const REAL_TIME: ChannelId = ChannelId(1);
     pub const FIXED_RATE_50: ChannelId = ChannelId(2);
     pub const FIXED_RATE_200: ChannelId = ChannelId(3);
+    pub const FIXED_RATE_1000: ChannelId = ChannelId(4);
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]

+ 4 - 1
lazer/sdk/rust/protocol/src/time.rs

@@ -499,12 +499,15 @@ impl FixedRate {
     pub const RATE_200_MS: Self = Self {
         rate: DurationUs::from_millis_u32(200),
     };
+    pub const RATE_1000_MS: Self = Self {
+        rate: DurationUs::from_millis_u32(1000),
+    };
 
     // Assumptions (tested below):
     // - Values are sorted.
     // - 1 second contains a whole number of each interval.
     // - all intervals are divisable by the smallest interval.
-    pub const ALL: [Self; 2] = [Self::RATE_50_MS, Self::RATE_200_MS];
+    pub const ALL: [Self; 3] = [Self::RATE_50_MS, Self::RATE_200_MS, Self::RATE_1000_MS];
     pub const MIN: Self = Self::ALL[0];
 
     pub fn from_millis(millis: u32) -> Option<Self> {

+ 4 - 4
packages/component-library/src/Skeleton/index.module.scss

@@ -11,13 +11,13 @@
   }
 }
 
-.fullSkeleton {
-  display: inline-block;
-}
-
 .skeleton {
   border-radius: theme.border-radius("lg");
 
+  &.fullSkeleton {
+    width: 100%;
+  }
+
   .skeletonInner {
     display: inline flow-root;
     width: calc(theme.spacing(1) * var(--skeleton-width));

+ 6 - 1
packages/component-library/src/Skeleton/index.stories.tsx

@@ -28,9 +28,14 @@ const meta = {
 export default meta;
 
 export const Skeleton = {
+  render: (args) => (
+    <div style={{ width: "100vw", display: "flex", justifyContent: "center" }}>
+      <SkeletonComponent {...args} />
+    </div>
+  ),
   args: {
     label: "Loading",
-    width: 20,
     fill: false,
+    width: 20,
   },
 } satisfies StoryObj<typeof SkeletonComponent>;

+ 20 - 14
packages/component-library/src/Skeleton/index.tsx

@@ -11,22 +11,28 @@ type Props = Omit<ComponentProps<"span">, "children"> & {
   fill?: boolean | undefined;
 };
 
-export const Skeleton = ({ className, label, width, fill, ...props }: Props) =>
-  fill ? (
-    <span className={clsx(styles.fullSkeleton, className)} {...props}>
+export const Skeleton = ({
+  className,
+  label,
+  width,
+  fill,
+  ...props
+}: Props) => (
+  <span
+    data-fill-width={width === undefined ? "" : undefined}
+    {...(width &&
+      !fill && { style: { "--skeleton-width": width } as CSSProperties })}
+    className={clsx(
+      styles.skeleton,
+      { [styles.fullSkeleton ?? ""]: fill },
+      { [className ?? ""]: fill },
+    )}
+  >
+    <span className={clsx(styles.skeletonInner, className)} {...props}>
       <Label>{label ?? "Loading"}</Label>
     </span>
-  ) : (
-    <span
-      data-fill-width={width === undefined ? "" : undefined}
-      {...(width && { style: { "--skeleton-width": width } as CSSProperties })}
-      className={clsx(styles.skeleton, { [className ?? ""]: fill })}
-    >
-      <span className={clsx(styles.skeletonInner, className)} {...props}>
-        <Label>{label ?? "Loading"}</Label>
-      </span>
-    </span>
-  );
+  </span>
+);
 
 const Label = ({ children }: { children: string | undefined }) => (
   <span className={styles.skeletonLabel}>{children ?? "Loading"}</span>

+ 240 - 83
pnpm-lock.yaml

@@ -1307,7 +1307,7 @@ importers:
         version: 0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)
       '@solana/wallet-adapter-wallets':
         specifier: 'catalog:'
-        version: 0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.4)
+        version: 0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.7.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.4)
       '@solana/web3.js':
         specifier: 'catalog:'
         version: 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
@@ -1887,7 +1887,7 @@ importers:
         version: 0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)
       '@solana/wallet-adapter-wallets':
         specifier: 'catalog:'
-        version: 0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)
+        version: 0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.7.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)
       '@solana/web3.js':
         specifier: ^1.73.0
         version: 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
@@ -2074,6 +2074,9 @@ importers:
       '@isaacs/ttlcache':
         specifier: ^1.4.1
         version: 1.4.1
+      cross-fetch:
+        specifier: ^4.0.0
+        version: 4.1.0(encoding@0.1.13)
       isomorphic-ws:
         specifier: ^5.0.0
         version: 5.0.0(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.3))
@@ -26737,6 +26740,17 @@ snapshots:
       '@ethersproject/properties': 5.8.0
       '@ethersproject/strings': 5.8.0
 
+  '@everstake/wallet-sdk-solana@2.0.9(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana-program/compute-budget': 0.6.1(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana-program/stake': 0.1.0(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana-program/system': 0.6.2(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - typescript
+      - ws
+
   '@everstake/wallet-sdk-solana@2.0.9(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana-program/compute-budget': 0.6.1(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
@@ -28250,12 +28264,12 @@ snapshots:
       '@types/yargs': 17.0.33
       chalk: 4.1.2
 
-  '@jnwng/walletconnect-solana@0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
+  '@jnwng/walletconnect-solana@0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
     dependencies:
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
       '@walletconnect/qrcode-modal': 1.8.0
-      '@walletconnect/sign-client': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
-      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@walletconnect/sign-client': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
       bs58: 5.0.0
     transitivePeerDependencies:
       - '@azure/app-configuration'
@@ -28281,11 +28295,11 @@ snapshots:
       - utf-8-validate
       - zod
 
-  '@jnwng/walletconnect-solana@0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)':
+  '@jnwng/walletconnect-solana@0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)':
     dependencies:
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
       '@walletconnect/qrcode-modal': 1.8.0
-      '@walletconnect/sign-client': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
+      '@walletconnect/sign-client': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
       '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
       bs58: 5.0.0
     transitivePeerDependencies:
@@ -32952,31 +32966,60 @@ snapshots:
       - react
       - react-native
 
+  '@solana-program/compute-budget@0.6.1(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+    dependencies:
+      '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
   '@solana-program/compute-budget@0.6.1(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
     dependencies:
       '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
 
+  '@solana-program/compute-budget@0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+    dependencies:
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
   '@solana-program/compute-budget@0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
     dependencies:
       '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
 
+  '@solana-program/stake@0.1.0(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+    dependencies:
+      '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
   '@solana-program/stake@0.1.0(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
     dependencies:
       '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
 
+  '@solana-program/system@0.6.2(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+    dependencies:
+      '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
   '@solana-program/system@0.6.2(@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
     dependencies:
       '@solana/web3.js': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
 
+  '@solana-program/system@0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+    dependencies:
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
   '@solana-program/system@0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
     dependencies:
       '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
 
+  '@solana-program/token-2022@0.4.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))':
+    dependencies:
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/sysvars': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+
   '@solana-program/token-2022@0.4.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))':
     dependencies:
       '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       '@solana/sysvars': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
 
+  '@solana-program/token@0.5.1(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+    dependencies:
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
   '@solana-program/token@0.5.1(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
     dependencies:
       '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -33316,6 +33359,31 @@ snapshots:
     transitivePeerDependencies:
       - fastestsmallesttextencoderdecoder
 
+  '@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/accounts': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/addresses': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/codecs': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/errors': 2.1.0(typescript@5.8.2)
+      '@solana/functional': 2.1.0(typescript@5.8.2)
+      '@solana/instructions': 2.1.0(typescript@5.8.2)
+      '@solana/keys': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/programs': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-parsed-types': 2.1.0(typescript@5.8.2)
+      '@solana/rpc-spec-types': 2.1.0(typescript@5.8.2)
+      '@solana/rpc-subscriptions': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/rpc-types': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/signers': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/sysvars': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transaction-confirmation': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/transaction-messages': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transactions': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+
   '@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/accounts': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
@@ -33502,6 +33570,15 @@ snapshots:
     transitivePeerDependencies:
       - fastestsmallesttextencoderdecoder
 
+  '@solana/rpc-subscriptions-channel-websocket@2.0.0(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/errors': 2.0.0(typescript@5.8.2)
+      '@solana/functional': 2.0.0(typescript@5.8.2)
+      '@solana/rpc-subscriptions-spec': 2.0.0(typescript@5.8.2)
+      '@solana/subscribable': 2.0.0(typescript@5.8.2)
+      typescript: 5.8.2
+      ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
   '@solana/rpc-subscriptions-channel-websocket@2.0.0(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/errors': 2.0.0(typescript@5.8.2)
@@ -33511,6 +33588,15 @@ snapshots:
       typescript: 5.8.2
       ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
 
+  '@solana/rpc-subscriptions-channel-websocket@2.1.0(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/errors': 2.1.0(typescript@5.8.2)
+      '@solana/functional': 2.1.0(typescript@5.8.2)
+      '@solana/rpc-subscriptions-spec': 2.1.0(typescript@5.8.2)
+      '@solana/subscribable': 2.1.0(typescript@5.8.2)
+      typescript: 5.8.2
+      ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
   '@solana/rpc-subscriptions-channel-websocket@2.1.0(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/errors': 2.1.0(typescript@5.8.2)
@@ -33536,6 +33622,24 @@ snapshots:
       '@solana/subscribable': 2.1.0(typescript@5.8.2)
       typescript: 5.8.2
 
+  '@solana/rpc-subscriptions@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/errors': 2.0.0(typescript@5.8.2)
+      '@solana/fast-stable-stringify': 2.0.0(typescript@5.8.2)
+      '@solana/functional': 2.0.0(typescript@5.8.2)
+      '@solana/promises': 2.0.0(typescript@5.8.2)
+      '@solana/rpc-spec-types': 2.0.0(typescript@5.8.2)
+      '@solana/rpc-subscriptions-api': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-subscriptions-channel-websocket': 2.0.0(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/rpc-subscriptions-spec': 2.0.0(typescript@5.8.2)
+      '@solana/rpc-transformers': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-types': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/subscribable': 2.0.0(typescript@5.8.2)
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+
   '@solana/rpc-subscriptions@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/errors': 2.0.0(typescript@5.8.2)
@@ -33554,6 +33658,24 @@ snapshots:
       - fastestsmallesttextencoderdecoder
       - ws
 
+  '@solana/rpc-subscriptions@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/errors': 2.1.0(typescript@5.8.2)
+      '@solana/fast-stable-stringify': 2.1.0(typescript@5.8.2)
+      '@solana/functional': 2.1.0(typescript@5.8.2)
+      '@solana/promises': 2.1.0(typescript@5.8.2)
+      '@solana/rpc-spec-types': 2.1.0(typescript@5.8.2)
+      '@solana/rpc-subscriptions-api': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-subscriptions-channel-websocket': 2.1.0(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/rpc-subscriptions-spec': 2.1.0(typescript@5.8.2)
+      '@solana/rpc-transformers': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-types': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/subscribable': 2.1.0(typescript@5.8.2)
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+
   '@solana/rpc-subscriptions@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/errors': 2.1.0(typescript@5.8.2)
@@ -33824,6 +33946,23 @@ snapshots:
     transitivePeerDependencies:
       - fastestsmallesttextencoderdecoder
 
+  '@solana/transaction-confirmation@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/addresses': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/codecs-strings': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/errors': 2.0.0(typescript@5.8.2)
+      '@solana/keys': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/promises': 2.0.0(typescript@5.8.2)
+      '@solana/rpc': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-subscriptions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/rpc-types': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transaction-messages': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transactions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+
   '@solana/transaction-confirmation@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/addresses': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
@@ -33841,6 +33980,23 @@ snapshots:
       - fastestsmallesttextencoderdecoder
       - ws
 
+  '@solana/transaction-confirmation@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/addresses': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/codecs-strings': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/errors': 2.1.0(typescript@5.8.2)
+      '@solana/keys': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/promises': 2.1.0(typescript@5.8.2)
+      '@solana/rpc': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-subscriptions': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/rpc-types': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transaction-messages': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transactions': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+
   '@solana/transaction-confirmation@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/addresses': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
@@ -34214,11 +34370,11 @@ snapshots:
       - utf-8-validate
       - ws
 
-  '@solana/wallet-adapter-trezor@0.1.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+  '@solana/wallet-adapter-trezor@0.1.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
-      '@trezor/connect-web': 9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@trezor/connect-web': 9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       buffer: 6.0.3
     transitivePeerDependencies:
       - '@solana/sysvars'
@@ -34247,9 +34403,9 @@ snapshots:
       '@solana/wallet-standard-util': 1.1.2
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
 
-  '@solana/wallet-adapter-walletconnect@0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
+  '@solana/wallet-adapter-walletconnect@0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
     dependencies:
-      '@jnwng/walletconnect-solana': 0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@jnwng/walletconnect-solana': 0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
       '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
     transitivePeerDependencies:
@@ -34276,9 +34432,9 @@ snapshots:
       - utf-8-validate
       - zod
 
-  '@solana/wallet-adapter-walletconnect@0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)':
+  '@solana/wallet-adapter-walletconnect@0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)':
     dependencies:
-      '@jnwng/walletconnect-solana': 0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
+      '@jnwng/walletconnect-solana': 0.2.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
       '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
     transitivePeerDependencies:
@@ -34305,7 +34461,7 @@ snapshots:
       - utf-8-validate
       - zod
 
-  '@solana/wallet-adapter-wallets@0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)':
+  '@solana/wallet-adapter-wallets@0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.7.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)':
     dependencies:
       '@solana/wallet-adapter-alpha': 0.1.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-avana': 0.1.14(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
@@ -34341,7 +34497,7 @@ snapshots:
       '@solana/wallet-adapter-trezor': 0.1.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-trust': 0.1.14(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-unsafe-burner': 0.1.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
-      '@solana/wallet-adapter-walletconnect': 0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@solana/wallet-adapter-walletconnect': 0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
       '@solana/wallet-adapter-xdefi': 0.1.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
     transitivePeerDependencies:
@@ -34382,7 +34538,7 @@ snapshots:
       - ws
       - zod
 
-  '@solana/wallet-adapter-wallets@0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.4)':
+  '@solana/wallet-adapter-wallets@0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.7.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.4)':
     dependencies:
       '@solana/wallet-adapter-alpha': 0.1.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-avana': 0.1.14(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
@@ -34415,10 +34571,10 @@ snapshots:
       '@solana/wallet-adapter-tokenary': 0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-tokenpocket': 0.4.20(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-torus': 0.11.29(@babel/runtime@7.27.0)(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
-      '@solana/wallet-adapter-trezor': 0.1.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/wallet-adapter-trezor': 0.1.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-trust': 0.1.14(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/wallet-adapter-unsafe-burner': 0.1.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
-      '@solana/wallet-adapter-walletconnect': 0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
+      '@solana/wallet-adapter-walletconnect': 0.1.17(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
       '@solana/wallet-adapter-xdefi': 0.1.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))
       '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
     transitivePeerDependencies:
@@ -34641,6 +34797,31 @@ snapshots:
       - encoding
       - utf-8-validate
 
+  '@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/accounts': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/addresses': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/codecs': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/errors': 2.0.0(typescript@5.8.2)
+      '@solana/functional': 2.0.0(typescript@5.8.2)
+      '@solana/instructions': 2.0.0(typescript@5.8.2)
+      '@solana/keys': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/programs': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/rpc-parsed-types': 2.0.0(typescript@5.8.2)
+      '@solana/rpc-spec-types': 2.0.0(typescript@5.8.2)
+      '@solana/rpc-subscriptions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/rpc-types': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/signers': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/sysvars': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transaction-confirmation': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana/transaction-messages': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      '@solana/transactions': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
+      typescript: 5.8.2
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - ws
+
   '@solana/web3.js@2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/accounts': 2.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)
@@ -35590,6 +35771,17 @@ snapshots:
       - expo-localization
       - react-native
 
+  '@trezor/blockchain-link-types@1.3.3(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+    dependencies:
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@trezor/type-utils': 1.1.5
+      '@trezor/utxo-lib': 2.3.3(tslib@2.8.1)
+      tslib: 2.8.1
+    transitivePeerDependencies:
+      - fastestsmallesttextencoderdecoder
+      - typescript
+      - ws
+
   '@trezor/blockchain-link-types@1.3.3(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -35652,13 +35844,13 @@ snapshots:
       - utf-8-validate
       - ws
 
-  '@trezor/blockchain-link@2.4.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+  '@trezor/blockchain-link@2.4.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
-      '@everstake/wallet-sdk-solana': 2.0.9(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
-      '@solana-program/token': 0.5.1(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
-      '@solana-program/token-2022': 0.4.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))
-      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
-      '@trezor/blockchain-link-types': 1.3.3(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@everstake/wallet-sdk-solana': 2.0.9(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana-program/token': 0.5.1(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana-program/token-2022': 0.4.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@trezor/blockchain-link-types': 1.3.3(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       '@trezor/blockchain-link-utils': 1.3.3(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)
       '@trezor/env-utils': 1.3.2(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)
       '@trezor/utils': 9.3.3(tslib@2.8.1)
@@ -35738,9 +35930,9 @@ snapshots:
       - utf-8-validate
       - ws
 
-  '@trezor/connect-web@9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+  '@trezor/connect-web@9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
-      '@trezor/connect': 9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@trezor/connect': 9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       '@trezor/connect-common': 0.3.3(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)
       '@trezor/utils': 9.3.3(tslib@2.8.1)
       tslib: 2.8.1
@@ -35801,7 +35993,7 @@ snapshots:
       - utf-8-validate
       - ws
 
-  '@trezor/connect@9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+  '@trezor/connect@9.5.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
     dependencies:
       '@ethereumjs/common': 4.4.0
       '@ethereumjs/tx': 5.4.0
@@ -35809,13 +36001,13 @@ snapshots:
       '@mobily/ts-belt': 3.13.1
       '@noble/hashes': 1.8.0
       '@scure/bip39': 1.6.0
-      '@solana-program/compute-budget': 0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
-      '@solana-program/system': 0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
-      '@solana-program/token': 0.5.1(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
-      '@solana-program/token-2022': 0.4.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))
-      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
-      '@trezor/blockchain-link': 2.4.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
-      '@trezor/blockchain-link-types': 1.3.3(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@solana-program/compute-budget': 0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana-program/system': 0.7.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana-program/token': 0.5.1(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+      '@solana-program/token-2022': 0.4.0(@solana/kit@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))
+      '@solana/kit': 2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@trezor/blockchain-link': 2.4.3(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+      '@trezor/blockchain-link-types': 1.3.3(fastestsmallesttextencoderdecoder@1.0.22)(tslib@2.8.1)(typescript@5.8.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
       '@trezor/blockchain-link-utils': 1.3.3(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)
       '@trezor/connect-analytics': 1.3.2(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)
       '@trezor/connect-common': 0.3.3(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(tslib@2.8.1)
@@ -37354,21 +37546,21 @@ snapshots:
       '@walletconnect/window-metadata': 1.0.0
       detect-browser: 5.2.0
 
-  '@walletconnect/core@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
+  '@walletconnect/core@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
     dependencies:
       '@walletconnect/heartbeat': 1.2.2
       '@walletconnect/jsonrpc-provider': 1.0.14
       '@walletconnect/jsonrpc-types': 1.0.4
       '@walletconnect/jsonrpc-utils': 1.0.8
       '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)
-      '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))
+      '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
       '@walletconnect/logger': 2.1.2
       '@walletconnect/relay-api': 1.0.11
       '@walletconnect/relay-auth': 1.1.0
       '@walletconnect/safe-json': 1.0.2
       '@walletconnect/time': 1.0.2
-      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))
-      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
+      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
       '@walletconnect/window-getters': 1.0.1
       es-toolkit: 1.33.0
       events: 3.3.0
@@ -37531,7 +37723,7 @@ snapshots:
       - bufferutil
       - utf-8-validate
 
-  '@walletconnect/keyvaluestorage@1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))':
+  '@walletconnect/keyvaluestorage@1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)':
     dependencies:
       '@walletconnect/safe-json': 1.0.2
       idb-keyval: 6.2.1
@@ -37642,16 +37834,16 @@ snapshots:
     dependencies:
       tslib: 1.14.1
 
-  '@walletconnect/sign-client@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
+  '@walletconnect/sign-client@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
     dependencies:
-      '@walletconnect/core': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@walletconnect/core': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
       '@walletconnect/events': 1.0.1
       '@walletconnect/heartbeat': 1.2.2
       '@walletconnect/jsonrpc-utils': 1.0.8
       '@walletconnect/logger': 2.1.2
       '@walletconnect/time': 1.0.2
-      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))
-      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
+      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
+      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)
       events: 3.3.0
     transitivePeerDependencies:
       - '@azure/app-configuration'
@@ -37712,53 +37904,18 @@ snapshots:
       - utf-8-validate
       - zod
 
-  '@walletconnect/sign-client@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)':
-    dependencies:
-      '@walletconnect/core': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
-      '@walletconnect/events': 1.0.1
-      '@walletconnect/heartbeat': 1.2.2
-      '@walletconnect/jsonrpc-utils': 1.0.8
-      '@walletconnect/logger': 2.1.2
-      '@walletconnect/time': 1.0.2
-      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
-      '@walletconnect/utils': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4)
-      events: 3.3.0
-    transitivePeerDependencies:
-      - '@azure/app-configuration'
-      - '@azure/cosmos'
-      - '@azure/data-tables'
-      - '@azure/identity'
-      - '@azure/keyvault-secrets'
-      - '@azure/storage-blob'
-      - '@capacitor/preferences'
-      - '@deno/kv'
-      - '@netlify/blobs'
-      - '@planetscale/database'
-      - '@react-native-async-storage/async-storage'
-      - '@upstash/redis'
-      - '@vercel/blob'
-      - '@vercel/kv'
-      - aws4fetch
-      - bufferutil
-      - db0
-      - ioredis
-      - typescript
-      - uploadthing
-      - utf-8-validate
-      - zod
-
   '@walletconnect/time@1.0.2':
     dependencies:
       tslib: 1.14.1
 
   '@walletconnect/types@1.8.0': {}
 
-  '@walletconnect/types@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))':
+  '@walletconnect/types@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)':
     dependencies:
       '@walletconnect/events': 1.0.1
       '@walletconnect/heartbeat': 1.2.2
       '@walletconnect/jsonrpc-types': 1.0.4
-      '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))
+      '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
       '@walletconnect/logger': 2.1.2
       events: 3.3.0
     transitivePeerDependencies:
@@ -37848,18 +38005,18 @@ snapshots:
       - utf-8-validate
       - zod
 
-  '@walletconnect/utils@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
+  '@walletconnect/utils@2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(ioredis@5.7.0)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)':
     dependencies:
       '@noble/ciphers': 1.2.1
       '@noble/curves': 1.8.1
       '@noble/hashes': 1.7.1
       '@walletconnect/jsonrpc-utils': 1.0.8
-      '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))
+      '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
       '@walletconnect/relay-api': 1.0.11
       '@walletconnect/relay-auth': 1.1.0
       '@walletconnect/safe-json': 1.0.2
       '@walletconnect/time': 1.0.2
-      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))
+      '@walletconnect/types': 2.19.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(ioredis@5.7.0)
       '@walletconnect/window-getters': 1.0.1
       '@walletconnect/window-metadata': 1.0.1
       bs58: 6.0.0