diff --git a/README.md b/README.md index 8a249d9..9bd5af3 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ One wallet, 41+ models, zero API keys. [![USDC Hackathon Winner](https://img.shields.io/badge/šŸ†_USDC_Hackathon-Agentic_Commerce_Winner-gold?style=flat-square)](https://x.com/USDC/status/2021625822294216977) [![x402 Protocol](https://img.shields.io/badge/x402-Micropayments-purple?style=flat-square)](https://x402.org) [![Base Network](https://img.shields.io/badge/Base-USDC-0052FF?style=flat-square&logo=coinbase&logoColor=white)](https://base.org) +[![Solana](https://img.shields.io/badge/Solana-USDC-9945FF?style=flat-square&logo=solana&logoColor=white)](https://solana.com) [![OpenClaw Plugin](https://img.shields.io/badge/OpenClaw-Plugin-orange?style=flat-square)](https://openclaw.ai) [![Telegram](https://img.shields.io/badge/Telegram-Community-26A5E4?style=flat-square&logo=telegram)](https://t.me/blockrunAI) @@ -55,7 +56,7 @@ One wallet, 41+ models, zero API keys. curl -fsSL https://blockrun.ai/ClawRouter-update | bash openclaw gateway restart -# 2. Fund your wallet with USDC on Base (address printed on install) +# 2. Fund your wallet with USDC on Base or Solana (address printed on install) # $5 is enough for thousands of requests ``` @@ -176,18 +177,22 @@ No account. No API key. **Payment IS authentication** via [x402](https://x402.or Request → 402 (price: $0.003) → wallet signs USDC → retry → response ``` -USDC stays in your wallet until spent — non-custodial. Price is visible in the 402 header before signing. +USDC stays in your wallet until spent - non-custodial. Price is visible in the 402 header before signing. + +**Dual-chain support:** Pay with USDC on **Base (EVM)** or **Solana**. Both wallets are derived from a single BIP-39 mnemonic on first run. Existing EVM-only users can enable Solana with `/wallet setup-solana`. ```bash -/wallet # Check balance and address -/stats # View usage and savings +/wallet # Check balance and address (both chains) +/wallet export # Export keys and mnemonic for backup +/wallet setup-solana # Enable Solana payments (existing users) +/stats # View usage and savings ``` **Fund your wallet:** -- **Coinbase:** Buy USDC, send to Base -- **Bridge:** Move USDC from any chain to Base -- **CEX:** Withdraw USDC to Base network +- **Base (EVM):** Send USDC on Base to your EVM address +- **Solana:** Send USDC on Solana to your Solana address +- **Coinbase/CEX:** Withdraw USDC to either network --- @@ -195,11 +200,12 @@ USDC stays in your wallet until spent — non-custodial. Price is visible in the For basic usage, no configuration needed. For advanced options: -| Variable | Default | Description | -| --------------------- | -------------- | ----------------------- | -| `BLOCKRUN_WALLET_KEY` | auto-generated | Your wallet private key | -| `BLOCKRUN_PROXY_PORT` | `8402` | Local proxy port | -| `CLAWROUTER_DISABLED` | `false` | Disable smart routing | +| Variable | Default | Description | +| --------------------------- | ------------------------------------ | ------------------------- | +| `BLOCKRUN_WALLET_KEY` | auto-generated | Your wallet private key | +| `BLOCKRUN_PROXY_PORT` | `8402` | Local proxy port | +| `CLAWROUTER_DISABLED` | `false` | Disable smart routing | +| `CLAWROUTER_SOLANA_RPC_URL` | `https://api.mainnet-beta.solana.com`| Solana RPC endpoint | **Full reference:** [docs/configuration.md](docs/configuration.md) diff --git a/package-lock.json b/package-lock.json index 32e79c6..9332526 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,20 @@ { "name": "@blockrun/clawrouter", - "version": "0.10.4", + "version": "0.10.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@blockrun/clawrouter", - "version": "0.10.4", + "version": "0.10.10", "license": "MIT", "dependencies": { + "@scure/bip32": "^1.6.0", + "@scure/bip39": "^1.5.0", + "@solana/kit": "^6.0.0", + "@x402/evm": "^2.4.0", + "@x402/fetch": "^2.4.0", + "@x402/svm": "^2.4.0", "viem": "^2.39.3" }, "bin": { @@ -1973,7 +1979,6 @@ "integrity": "sha512-xRlzazC+QZwr6z4ixEqYHo9fgwhTZ3xNSdljlKfUFGZSdlvt166DljRELFUfFytlYOYvo3vTisA/AFOuOAzFQQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2668,7 +2673,6 @@ "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.1.1" } @@ -2678,8 +2682,7 @@ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@line/bot-sdk": { "version": "10.6.0", @@ -3155,6 +3158,7 @@ "integrity": "sha512-7GjmkMirJHejeALCqUnZY3QwID7bbumOiLrqq2LKgxrdjdmxWQBTc6rcASa2u8wuWrH7qo4/4n/VNrOwCoKlKg==", "dev": true, "license": "MIT", + "peer": true, "workspaces": [ "e2e/*" ], @@ -3463,7 +3467,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3482,7 +3485,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3500,7 +3502,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3518,7 +3519,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3536,7 +3536,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3554,7 +3553,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3573,7 +3571,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3591,7 +3588,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3610,7 +3606,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3628,7 +3623,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3646,7 +3640,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3664,7 +3657,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3682,7 +3674,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=20.0.0" } @@ -3693,7 +3684,6 @@ "integrity": "sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-app": "^8.1.2", "@octokit/auth-unauthenticated": "^7.0.3", @@ -3713,7 +3703,6 @@ "integrity": "sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-oauth-app": "^9.0.3", "@octokit/auth-oauth-user": "^6.0.2", @@ -3734,7 +3723,6 @@ "integrity": "sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/auth-oauth-user": "^6.0.2", @@ -3752,7 +3740,6 @@ "integrity": "sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", @@ -3769,7 +3756,6 @@ "integrity": "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/oauth-methods": "^6.0.2", @@ -3787,7 +3773,6 @@ "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 20" } @@ -3798,7 +3783,6 @@ "integrity": "sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0" @@ -3833,7 +3817,6 @@ "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" @@ -3848,7 +3831,6 @@ "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", @@ -3864,7 +3846,6 @@ "integrity": "sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-oauth-app": "^9.0.2", "@octokit/auth-oauth-user": "^6.0.1", @@ -3885,7 +3866,6 @@ "integrity": "sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 20" } @@ -3896,7 +3876,6 @@ "integrity": "sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/oauth-authorization-url": "^8.0.0", "@octokit/request": "^10.0.6", @@ -3912,16 +3891,14 @@ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@octokit/openapi-webhooks-types": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", "integrity": "sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-graphql": { "version": "6.0.0", @@ -3929,7 +3906,6 @@ "integrity": "sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 20" }, @@ -3943,7 +3919,6 @@ "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, @@ -3960,7 +3935,6 @@ "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, @@ -3977,7 +3951,6 @@ "integrity": "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", @@ -3996,7 +3969,6 @@ "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" @@ -4014,7 +3986,6 @@ "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", @@ -4032,7 +4003,6 @@ "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, @@ -4046,7 +4016,6 @@ "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/openapi-types": "^27.0.0" } @@ -4057,7 +4026,6 @@ "integrity": "sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/openapi-webhooks-types": "12.1.0", "@octokit/request-error": "^7.0.0", @@ -4073,7 +4041,6 @@ "integrity": "sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 20" } @@ -4177,7 +4144,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10" }, @@ -4205,7 +4171,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4223,7 +4188,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4241,7 +4205,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4259,7 +4222,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4277,7 +4239,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4295,7 +4256,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4313,7 +4273,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -4331,7 +4290,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -5544,684 +5502,2736 @@ "node": ">=18.0.0" } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tinyhttp/content-disposition": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.4.tgz", - "integrity": "sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==", - "dev": true, + "node_modules/@solana/accounts": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/accounts/-/accounts-6.1.0.tgz", + "integrity": "sha512-0jhmhSSS71ClLtBQIDrLlhkiNER4M9RIXTl1eJ1yJoFlE608JaKHTjNWsdVKdke7uBD6exdjNZkIVmouQPHMcA==", "license": "MIT", - "peer": true, + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/rpc-spec": "6.1.0", + "@solana/rpc-types": "6.1.0" + }, "engines": { - "node": ">=12.17.0" + "node": ">=20.18.0" }, - "funding": { - "type": "individual", - "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@tokenizer/inflate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", - "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", - "dev": true, + "node_modules/@solana/addresses": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/addresses/-/addresses-6.1.0.tgz", + "integrity": "sha512-QT04Vie4iICaalQQRJFMGj/P56IxXiwFtVuZHu1qjZUNmuGTOvX6G98b27RaGtLzpJ3NIku/6OtKxLUBqAKAyQ==", "license": "MIT", "dependencies": { - "debug": "^4.4.3", - "token-types": "^6.1.1" + "@solana/assertions": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/nominal-types": "6.1.0" }, "engines": { - "node": ">=18" + "node": ">=20.18.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/aws-lambda": { - "version": "8.10.160", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", - "integrity": "sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==", - "dev": true, + "node_modules/@solana/assertions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/assertions/-/assertions-6.1.0.tgz", + "integrity": "sha512-pLgxB2xxTk2QfTaWpnRpSMYgaPkKYDQgptRvbwmuDQnOW1Zopg+42MT2UrDGd3UFMML1uOFPxIwKM6m51H0uXw==", "license": "MIT", - "peer": true + "dependencies": { + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, + "node_modules/@solana/codecs": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/codecs/-/codecs-6.1.0.tgz", + "integrity": "sha512-VHBS3t8fyVjE0Nqo6b4TUnzdwdRaVo+B5ufHhPLbbjkEXzz8HB4E/OBjgasn+zWGlfScfQAiBFOsfZjbVWu4XA==", "license": "MIT", - "peer": true, "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "@solana/codecs-core": "6.1.0", + "@solana/codecs-data-structures": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/options": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/bun": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.6.tgz", - "integrity": "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==", - "dev": true, + "node_modules/@solana/codecs-core": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-6.1.0.tgz", + "integrity": "sha512-5rNnDOOm2GRFMJbd9imYCPNvGOrQ+TZ53NCkFFWbbB7f+L9KkLeuuAsDMFN1lCziJFlymvN785YtDnMeWj2W+g==", "license": "MIT", - "optional": true, "dependencies": { - "bun-types": "1.3.6" + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, + "node_modules/@solana/codecs-data-structures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-6.1.0.tgz", + "integrity": "sha512-1cb9g5hrrucTuGkGxqVVq7dCwSMnn4YqwTe365iKkK8HBpLBmUl8XATf1MUs5UtDun1g9eNWOL72Psr8mIUqTQ==", "license": "MIT", "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "@solana/codecs-core": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, + "node_modules/@solana/codecs-numbers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-6.1.0.tgz", + "integrity": "sha512-YPQwwl6LE3igH23ah+d8kgpyE5xFcPbuwhxCDsLWqY/ESrvO/0YQSbsgIXahbhZxN59ZC4uq1LnHhBNbpCSVQg==", "license": "MIT", - "peer": true, "dependencies": { - "@types/node": "*" + "@solana/codecs-core": "6.1.0", + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, + "node_modules/@solana/codecs-strings": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-6.1.0.tgz", + "integrity": "sha512-pRH5uAn4VCFUs2rYiDITyWsRnpvs3Uh/nhSc6OSP/kusghcCcCJcUzHBIjT4x08MVacXmGUlSLe/9qPQO+QK3Q==", "license": "MIT", - "peer": true, "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" + "@solana/codecs-core": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "fastestsmallesttextencoderdecoder": "^1.0.22", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "fastestsmallesttextencoderdecoder": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, + "node_modules/@solana/errors": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-6.1.0.tgz", + "integrity": "sha512-cqSwcw3Rmn85UR7PyF5nKPdlQsRYBkx7YGRvFaJ6Sal1PM+bfolhL5iT7STQoXxdhXGYwHMPg7kZYxmMdjwnJA==", "license": "MIT", - "peer": true, "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "chalk": "5.6.2", + "commander": "14.0.3" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT", - "peer": true + "node_modules/@solana/errors/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@solana/fast-stable-stringify": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/fast-stable-stringify/-/fast-stable-stringify-6.1.0.tgz", + "integrity": "sha512-QXUfDFaJCFeARsxJgScWmJ153Tit7Cimk9y0UWWreNBr2Aphi67Nlcj/tr7UABTO0Qaw/0gwrK76zz3m1t3nIw==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/functional": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/functional/-/functional-6.1.0.tgz", + "integrity": "sha512-+Sm8ldVxSTHIKaZDvcBu81FPjknXx6OMPlakkKmXjKxPgVLl86ruqMo2yEwoDUHV7DysLrLLcRNn13rfulomRw==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/instruction-plans": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/instruction-plans/-/instruction-plans-6.1.0.tgz", + "integrity": "sha512-zcsHg544t1zn7LLOVUxOWYlsKn9gvT7R+pL3cTiP2wFNoUN0h9En87H6nVqkZ8LWw23asgW0uM5uJGwfBx2h1Q==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/instructions": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/promises": "6.1.0", + "@solana/transaction-messages": "6.1.0", + "@solana/transactions": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/instructions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/instructions/-/instructions-6.1.0.tgz", + "integrity": "sha512-w1LdbJ3yanESckNTYC5KPckgN/25FyGCm07WWrs+dCnnpRNeLiVHIytXCPmArOVAXVkOYidXzhWmqCzqKUjYaA==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "6.1.0", + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/keys": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/keys/-/keys-6.1.0.tgz", + "integrity": "sha512-C/SGCl3VOgBQZ0mLrMxCcJYnMsGpgE8wbx29jqRY+R91m5YhS1f/GfXJPR1lN/h7QGrJ6YDm8eI0Y3AZ7goKHg==", + "license": "MIT", + "dependencies": { + "@solana/assertions": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/nominal-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/kit": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/kit/-/kit-6.1.0.tgz", + "integrity": "sha512-24exn11BPonquufyCkGgypVtmN4JOsdGMsbF3EZ4kFyk7ZNryCn/N8eELr1FCVrHWRXoc0xy/HFaESBULTMf6g==", + "license": "MIT", + "dependencies": { + "@solana/accounts": "6.1.0", + "@solana/addresses": "6.1.0", + "@solana/codecs": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/instruction-plans": "6.1.0", + "@solana/instructions": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/offchain-messages": "6.1.0", + "@solana/plugin-core": "6.1.0", + "@solana/plugin-interfaces": "6.1.0", + "@solana/program-client-core": "6.1.0", + "@solana/programs": "6.1.0", + "@solana/rpc": "6.1.0", + "@solana/rpc-api": "6.1.0", + "@solana/rpc-parsed-types": "6.1.0", + "@solana/rpc-spec-types": "6.1.0", + "@solana/rpc-subscriptions": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/signers": "6.1.0", + "@solana/sysvars": "6.1.0", + "@solana/transaction-confirmation": "6.1.0", + "@solana/transaction-messages": "6.1.0", + "@solana/transactions": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/nominal-types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/nominal-types/-/nominal-types-6.1.0.tgz", + "integrity": "sha512-+skHjN0arNNB9TLsGqA94VCx7euyGURI+qG6wck6E4D7hH6i6DxGiVrtKRghx+smJkkLtTm9BvdVKGoeNQYr7Q==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/offchain-messages": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/offchain-messages/-/offchain-messages-6.1.0.tgz", + "integrity": "sha512-jrUb7HGUnRA+k44upcqKeevtEdqMxYRSlFdE0JTctZunGlP3GCcTl12tFOpbnFHvBLt8RwS62+nyeES8zzNwXA==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-data-structures": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/nominal-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/options": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-6.1.0.tgz", + "integrity": "sha512-/4FtVfR6nkHkMCumyh7/lJ6jMqyES6tKUbOJRa6gJxcIUWeRDu+XrHTHLf3gRNUqDAbFvW8FMIrQm7PdreZgRA==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "6.1.0", + "@solana/codecs-data-structures": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/plugin-core": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/plugin-core/-/plugin-core-6.1.0.tgz", + "integrity": "sha512-2nmNCPa6B1QArqpAZHWUkK6K7UXLTrekfcfJm2V//ATEtLpKEBlv0c3mrhOYwNAKP2TpNuvEV33InXWKst9oXQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/plugin-interfaces": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/plugin-interfaces/-/plugin-interfaces-6.1.0.tgz", + "integrity": "sha512-eWSzfOuwtHUp8vljf5V24Tkz3WxqxiV0vD/BJZBNRZMdYRw3Cw3oeWcvEqHHxGUOie6AjIK8GrKggi8F73ZXbg==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/instruction-plans": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/rpc-spec": "6.1.0", + "@solana/rpc-subscriptions-spec": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/signers": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/program-client-core": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/program-client-core/-/program-client-core-6.1.0.tgz", + "integrity": "sha512-5Apka+ulWNfLNLYNR63pLnr5XvkXTQWeaftWED93iTWTZrZv9SyFWcmIsaes6eqGXMQ3RhlebnrWODtKuAA62g==", + "license": "MIT", + "dependencies": { + "@solana/accounts": "6.1.0", + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/instruction-plans": "6.1.0", + "@solana/instructions": "6.1.0", + "@solana/plugin-interfaces": "6.1.0", + "@solana/rpc-api": "6.1.0", + "@solana/signers": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/programs": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/programs/-/programs-6.1.0.tgz", + "integrity": "sha512-i4L4gSlIHDsdYRt3/YKVKMIN3UuYSKHRqK9B+AejcIc0y6Y/AXnHqzmpBRXEhvTXz18nt59MLXpVU4wu7ASjJA==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/promises": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/promises/-/promises-6.1.0.tgz", + "integrity": "sha512-/mUW6peXQiEOaylLpGv4vtkvPzQvSbfhX9j5PNIK/ry4S3SHRQ3j3W/oGy4y3LR5alwo7NcVbubrkh4e4xwcww==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc/-/rpc-6.1.0.tgz", + "integrity": "sha512-R3y5PklW9mPy5Y34hsXj40R28zN2N7AGLnHqYJVkXkllwVub/QCNpSdDxAnbbS5EGOYGoUOW8s5LFoXwMSr1LQ==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/fast-stable-stringify": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/rpc-api": "6.1.0", + "@solana/rpc-spec": "6.1.0", + "@solana/rpc-spec-types": "6.1.0", + "@solana/rpc-transformers": "6.1.0", + "@solana/rpc-transport-http": "6.1.0", + "@solana/rpc-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-api/-/rpc-api-6.1.0.tgz", + "integrity": "sha512-+hO5+kZjJHuUNATUQxlJ1+ztXFkgn1j46zRwt3X7kF+VHkW3wsQ7up0JTS+Xsacmkrj1WKfymQweq8JTrsAG8A==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/rpc-parsed-types": "6.1.0", + "@solana/rpc-spec": "6.1.0", + "@solana/rpc-transformers": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/transaction-messages": "6.1.0", + "@solana/transactions": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-parsed-types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-parsed-types/-/rpc-parsed-types-6.1.0.tgz", + "integrity": "sha512-YKccynVgWt/gbs0tBYstNw6BSVuOeWdeAldTB2OgH95o2Q04DpO4v97X1MZDysA4SvSZM30Ek5Ni5ss3kskgdw==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-spec": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec/-/rpc-spec-6.1.0.tgz", + "integrity": "sha512-RxpkIGizCYhXGUcap7npV2S/rAXZ7P/liozY/ExjMmCxYTDwGIW33kp/uH/JRxuzrL8+f8FqY76VsqqIe+2VZw==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/rpc-spec-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-spec-types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec-types/-/rpc-spec-types-6.1.0.tgz", + "integrity": "sha512-tldMv1b6VGcvcRrY5MDWKlsyEKH6K96zE7gAIpKDX2G4T47ZOV+OMA3nh6xQpRgtyCUBsej0t80qmvTBDX/5IQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions/-/rpc-subscriptions-6.1.0.tgz", + "integrity": "sha512-sqwj+cQinWcZ7M/9+cudKxMPTkTQyGP73980vPCWM7vCpPkp2qzgrEie4DdgDGo+NMwIjeFgu2kdUuLHI3GD/g==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/fast-stable-stringify": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/promises": "6.1.0", + "@solana/rpc-spec-types": "6.1.0", + "@solana/rpc-subscriptions-api": "6.1.0", + "@solana/rpc-subscriptions-channel-websocket": "6.1.0", + "@solana/rpc-subscriptions-spec": "6.1.0", + "@solana/rpc-transformers": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/subscribable": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-6.1.0.tgz", + "integrity": "sha512-I6J+3VU0dda6EySKbDyd+1urC7RGIRPRp0DcWRVcy68NOLbq0I5C40Dn9O2Zf8iCdK4PbQ7JKdCvZ/bDd45hdg==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/rpc-subscriptions-spec": "6.1.0", + "@solana/rpc-transformers": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/transaction-messages": "6.1.0", + "@solana/transactions": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions-channel-websocket": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-6.1.0.tgz", + "integrity": "sha512-vsx9b+uyCr9L3giao/BTiBFA8DxV5+gDNFq0t5uL21uQ17JXzBektwzHuHoth9IjkvXV/h+IhwXfuLE9Qm4GQg==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/rpc-subscriptions-spec": "6.1.0", + "@solana/subscribable": "6.1.0", + "ws": "^8.19.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions-spec": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-6.1.0.tgz", + "integrity": "sha512-P06jhqzHpZGaLeJmIQkpDeMDD1xUp53ARpmXMsduMC+U5ZKQt29CLo+JrR18boNtls6WfttjVMEbzF25/4UPVA==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/promises": "6.1.0", + "@solana/rpc-spec-types": "6.1.0", + "@solana/subscribable": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-transformers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-transformers/-/rpc-transformers-6.1.0.tgz", + "integrity": "sha512-OsSuuRPmsmS02eR9Zz+4iTsr+21hvEMEex5vwbwN6LAGPFlQ4ohqGkxgZCwmYd+Q5HWpnn9Uuf1MDTLLrKQkig==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/nominal-types": "6.1.0", + "@solana/rpc-spec-types": "6.1.0", + "@solana/rpc-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-transport-http": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-transport-http/-/rpc-transport-http-6.1.0.tgz", + "integrity": "sha512-3ebaTYuglLJagaXtjwDPVI7SQeeeFN2fpetpGKsuMAiti4fzYqEkNN8FIo+nXBzqqG/cVc2421xKjXl6sO1k/g==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0", + "@solana/rpc-spec": "6.1.0", + "@solana/rpc-spec-types": "6.1.0", + "undici-types": "^7.21.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-transport-http/node_modules/undici-types": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.22.0.tgz", + "integrity": "sha512-RKZvifiL60xdsIuC80UY0dq8Z7DbJUV8/l2hOVbyZAxBzEeQU4Z58+4ZzJ6WN2Lidi9KzT5EbiGX+PI/UGYuRw==", + "license": "MIT" + }, + "node_modules/@solana/rpc-types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/rpc-types/-/rpc-types-6.1.0.tgz", + "integrity": "sha512-lR+Cb3v5Rpl49HsXWASy++TSE1AD86eRKabY+iuWnbBMYVGI4MamAvYwgBiygsCNc30nyO2TFNj9STMeSD/gAg==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/nominal-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/signers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/signers/-/signers-6.1.0.tgz", + "integrity": "sha512-WDPGZJr6jIe2dEChv/2KQBnaga8dqOjd6ceBj/HcDHxnCudo66t7GlyZ9+9jMO40AgOOb7EDE5FDqPMrHMg5Yw==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/instructions": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/nominal-types": "6.1.0", + "@solana/offchain-messages": "6.1.0", + "@solana/transaction-messages": "6.1.0", + "@solana/transactions": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/subscribable": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/subscribable/-/subscribable-6.1.0.tgz", + "integrity": "sha512-HiUfkxN7638uxPmY4t0gI4+yqnFLZYJKFaT9EpWIuGrOB1d9n+uOHNs3NU7cVMwWXgfZUbztTCKyCVTbcwesNg==", + "license": "MIT", + "dependencies": { + "@solana/errors": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/sysvars": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/sysvars/-/sysvars-6.1.0.tgz", + "integrity": "sha512-KwJyBBrAOx0BgkiZqOKAaySDb/0JrUFSBQL9/O1kSKGy9TCRX55Ytr1HxNTcTPppWNpbM6JZVK+yW3Ruey0HRw==", + "license": "MIT", + "dependencies": { + "@solana/accounts": "6.1.0", + "@solana/codecs": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/rpc-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/transaction-confirmation": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/transaction-confirmation/-/transaction-confirmation-6.1.0.tgz", + "integrity": "sha512-akSjcqAMOGPFvKctFDSzhjcRc/45WbEVdVQ9mjgH6OYo7B11WZZZaeGPlzAw5KyuG34Px941xmICkBmNqEH47Q==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/promises": "6.1.0", + "@solana/rpc": "6.1.0", + "@solana/rpc-subscriptions": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/transaction-messages": "6.1.0", + "@solana/transactions": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/transaction-messages": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/transaction-messages/-/transaction-messages-6.1.0.tgz", + "integrity": "sha512-Dpv54LRVcfFbFEa/uB53LaY/TRfKuPGMKR7Z4F290zBgkj9xkpZkI+WLiJBiSloI7Qo2KZqXj3514BIeZvJLcg==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-data-structures": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/instructions": "6.1.0", + "@solana/nominal-types": "6.1.0", + "@solana/rpc-types": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/transactions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@solana/transactions/-/transactions-6.1.0.tgz", + "integrity": "sha512-1dkiNJcTtlHm4Fvs5VohNVpv7RbvbUYYKV7lYXMPIskoLF1eZp0tVlEqD/cRl91RNz7HEysfHqBAwlcJcRmrRg==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "6.1.0", + "@solana/codecs-core": "6.1.0", + "@solana/codecs-data-structures": "6.1.0", + "@solana/codecs-numbers": "6.1.0", + "@solana/codecs-strings": "6.1.0", + "@solana/errors": "6.1.0", + "@solana/functional": "6.1.0", + "@solana/instructions": "6.1.0", + "@solana/keys": "6.1.0", + "@solana/nominal-types": "6.1.0", + "@solana/rpc-types": "6.1.0", + "@solana/transaction-messages": "6.1.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@spruceid/siwe-parser": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.1.2.tgz", + "integrity": "sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "^1.1.2", + "apg-js": "^4.3.0", + "uri-js": "^4.4.1", + "valid-url": "^1.0.9" + } + }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "license": "MIT", + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "license": "MIT" + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.4.tgz", + "integrity": "sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.160", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", + "integrity": "sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bun": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.6.tgz", + "integrity": "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bun-types": "1.3.6" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", + "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", "dev": true, "license": "MIT", "dependencies": { - "@types/ms": "*", - "@types/node": "*" + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "node_modules/@whiskeysockets/baileys": { + "version": "7.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-7.0.0-rc.9.tgz", + "integrity": "sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==", "dev": true, - "license": "MIT" + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@cacheable/node-cache": "^1.4.0", + "@hapi/boom": "^9.1.3", + "async-mutex": "^0.5.0", + "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", + "lru-cache": "^11.1.0", + "music-metadata": "^11.7.0", + "p-queue": "^9.0.0", + "pino": "^9.6", + "protobufjs": "^7.2.4", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "audio-decode": "^2.1.3", + "jimp": "^1.6.0", + "link-preview-js": "^3.0.0", + "sharp": "*" + }, + "peerDependenciesMeta": { + "audio-decode": { + "optional": true + }, + "jimp": { + "optional": true + }, + "link-preview-js": { + "optional": true + } + } }, - "node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "node_modules/@whiskeysockets/baileys/node_modules/p-queue": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", + "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "node_modules/@whiskeysockets/baileys/node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@x402/core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@x402/core/-/core-2.4.0.tgz", + "integrity": "sha512-g4K5dAVjevQftxCcpFlUDjO2AHE43FkO43VxwLCQ8ET3ki4aj2fzCcgvnXEj2eloJoocFS/Evt4pSTnP/4cFJw==", + "license": "Apache-2.0", + "dependencies": { + "zod": "^3.24.2" + } + }, + "node_modules/@x402/evm": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@x402/evm/-/evm-2.4.0.tgz", + "integrity": "sha512-k9qIEhJ5m8jZLPA44hcLEP9I1WC8nF375A7pX/3XGPA+H2UPUoY8fzBaQA2U+4lMv/eIyfz05klSj/8LzP1saA==", + "license": "Apache-2.0", + "dependencies": { + "@x402/core": "~2.4.0", + "@x402/extensions": "~2.4.0", + "viem": "^2.39.3", + "zod": "^3.24.2" + } + }, + "node_modules/@x402/extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@x402/extensions/-/extensions-2.4.0.tgz", + "integrity": "sha512-tg/mSAS+NgwUaOdjt8agSjVkO1s9NYy+kSPubVlZf/Fy884qu7dSW81Ixu1NRYEz+vBk0rtz6b+eEvS0v72tMQ==", + "license": "Apache-2.0", + "dependencies": { + "@scure/base": "^1.2.6", + "@x402/core": "~2.4.0", + "ajv": "^8.17.1", + "siwe": "^2.3.2", + "tweetnacl": "^1.0.3", + "viem": "^2.43.5", + "zod": "^3.24.2" + } + }, + "node_modules/@x402/fetch": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@x402/fetch/-/fetch-2.4.0.tgz", + "integrity": "sha512-OOMaPAT85qcKnKWRvB4zZlC/CUod0YogCpbLmi555Puxeh7u69mA30MWkNsyzMfhKS1y+GHiG9eZZbpYFmLMTg==", + "license": "Apache-2.0", + "dependencies": { + "@x402/core": "~2.4.0", + "viem": "^2.39.3", + "zod": "^3.24.2" + } + }, + "node_modules/@x402/svm": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@x402/svm/-/svm-2.4.0.tgz", + "integrity": "sha512-mMZVIbEFEuQqM8hfiEau+PaxUKumZm+v5c0uc4Zu1H+0pLg35zrvPCt5/F3NVSQiidPg3CP37CXnxrtiH2tDVg==", + "license": "Apache-2.0", + "dependencies": { + "@solana-program/compute-budget": "^0.11.0", + "@solana-program/token": "^0.9.0", + "@solana-program/token-2022": "^0.6.1", + "@solana/kit": "^5.1.0", + "@x402/core": "~2.4.0" + } + }, + "node_modules/@x402/svm/node_modules/@solana-program/compute-budget": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@solana-program/compute-budget/-/compute-budget-0.11.0.tgz", + "integrity": "sha512-7f1ePqB/eURkTwTOO9TNIdUXZcyrZoX3Uy2hNo7cXMfNhPFWp9AVgIyRNBc2jf15sdUa9gNpW+PfP2iV8AYAaw==", + "license": "Apache-2.0", + "peerDependencies": { + "@solana/kit": "^5.0" + } + }, + "node_modules/@x402/svm/node_modules/@solana-program/token": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@solana-program/token/-/token-0.9.0.tgz", + "integrity": "sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==", + "license": "Apache-2.0", + "peerDependencies": { + "@solana/kit": "^5.0" + } + }, + "node_modules/@x402/svm/node_modules/@solana-program/token-2022": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@solana-program/token-2022/-/token-2022-0.6.1.tgz", + "integrity": "sha512-Ex02cruDMGfBMvZZCrggVR45vdQQSI/unHVpt/7HPt/IwFYB4eTlXtO8otYZyqV/ce5GqZ8S6uwyRf0zy6fdbA==", + "license": "Apache-2.0", + "peerDependencies": { + "@solana/kit": "^5.0", + "@solana/sysvars": "^5.0" + } + }, + "node_modules/@x402/svm/node_modules/@solana/accounts": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/accounts/-/accounts-5.5.1.tgz", + "integrity": "sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/addresses": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/addresses/-/addresses-5.5.1.tgz", + "integrity": "sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==", + "license": "MIT", + "dependencies": { + "@solana/assertions": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/assertions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/assertions/-/assertions-5.5.1.tgz", + "integrity": "sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/codecs": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs/-/codecs-5.5.1.tgz", + "integrity": "sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/options": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/codecs-core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-5.5.1.tgz", + "integrity": "sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/codecs-data-structures": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-5.5.1.tgz", + "integrity": "sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/codecs-numbers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-5.5.1.tgz", + "integrity": "sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/codecs-strings": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-5.5.1.tgz", + "integrity": "sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "fastestsmallesttextencoderdecoder": "^1.0.22", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "fastestsmallesttextencoderdecoder": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/errors": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-5.5.1.tgz", + "integrity": "sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==", + "license": "MIT", + "dependencies": { + "chalk": "5.6.2", + "commander": "14.0.2" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@types/node": { - "version": "25.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", - "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/fast-stable-stringify": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/fast-stable-stringify/-/fast-stable-stringify-5.5.1.tgz", + "integrity": "sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==", "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/functional": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/functional/-/functional-5.5.1.tgz", + "integrity": "sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==", "license": "MIT", - "peer": true + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/instruction-plans": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/instruction-plans/-/instruction-plans-5.5.1.tgz", + "integrity": "sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==", "license": "MIT", - "peer": true + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true, - "license": "MIT" + "node_modules/@x402/svm/node_modules/@solana/instructions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/instructions/-/instructions-5.5.1.tgz", + "integrity": "sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/keys": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/keys/-/keys-5.5.1.tgz", + "integrity": "sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==", "license": "MIT", - "peer": true, "dependencies": { - "@types/node": "*" + "@solana/assertions": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/kit": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/kit/-/kit-5.5.1.tgz", + "integrity": "sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==", "license": "MIT", "peer": true, "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" + "@solana/accounts": "5.5.1", + "@solana/addresses": "5.5.1", + "@solana/codecs": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instruction-plans": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/offchain-messages": "5.5.1", + "@solana/plugin-core": "5.5.1", + "@solana/programs": "5.5.1", + "@solana/rpc": "5.5.1", + "@solana/rpc-api": "5.5.1", + "@solana/rpc-parsed-types": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-subscriptions": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/signers": "5.5.1", + "@solana/sysvars": "5.5.1", + "@solana/transaction-confirmation": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/nominal-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/nominal-types/-/nominal-types-5.5.1.tgz", + "integrity": "sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==", "license": "MIT", - "dependencies": { - "@types/node": "*" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/offchain-messages": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/offchain-messages/-/offchain-messages-5.5.1.tgz", + "integrity": "sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==", "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=20.18.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.54.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/options": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-5.5.1.tgz", + "integrity": "sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3" + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/plugin-core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/plugin-core/-/plugin-core-5.5.1.tgz", + "integrity": "sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/programs": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/programs/-/programs-5.5.1.tgz", + "integrity": "sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", - "debug": "^4.4.3" + "@solana/addresses": "5.5.1", + "@solana/errors": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/promises": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/promises/-/promises-5.5.1.tgz", + "integrity": "sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc/-/rpc-5.5.1.tgz", + "integrity": "sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@solana/errors": "5.5.1", + "@solana/fast-stable-stringify": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/rpc-api": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-transport-http": "5.5.1", + "@solana/rpc-types": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-api": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-api/-/rpc-api-5.5.1.tgz", + "integrity": "sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==", "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/rpc-parsed-types": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@x402/svm/node_modules/@solana/rpc-parsed-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-parsed-types/-/rpc-parsed-types-5.5.1.tgz", + "integrity": "sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", - "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-spec": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec/-/rpc-spec-5.5.1.tgz", + "integrity": "sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "@solana/errors": "5.5.1", + "@solana/rpc-spec-types": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=20.18.0" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-spec-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec-types/-/rpc-spec-types-5.5.1.tgz", + "integrity": "sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==", "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-subscriptions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions/-/rpc-subscriptions-5.5.1.tgz", + "integrity": "sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "@solana/errors": "5.5.1", + "@solana/fast-stable-stringify": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-subscriptions-api": "5.5.1", + "@solana/rpc-subscriptions-channel-websocket": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/subscribable": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=20.18.0" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", + "node_modules/@x402/svm/node_modules/@solana/rpc-subscriptions-api": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-5.5.1.tgz", + "integrity": "sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "@solana/addresses": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=20.18.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-subscriptions-channel-websocket": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-5.5.1.tgz", + "integrity": "sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/subscribable": "5.5.1", + "ws": "^8.19.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=20.18.0" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-subscriptions-spec": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-5.5.1.tgz", + "integrity": "sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@solana/errors": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/subscribable": "5.5.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.18.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vitest/expect": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", - "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-transformers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-transformers/-/rpc-transformers-5.5.1.tgz", + "integrity": "sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==", "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "chai": "^6.2.1", - "tinyrainbow": "^3.0.3" + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-types": "5.5.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vitest/mocker": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", - "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-transport-http": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-transport-http/-/rpc-transport-http-5.5.1.tgz", + "integrity": "sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==", "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.18", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "@solana/errors": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "undici-types": "^7.19.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=20.18.0" }, "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "typescript": "^5.0.0" }, "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { + "typescript": { "optional": true } } }, - "node_modules/@vitest/pretty-format": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/rpc-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-types/-/rpc-types-5.5.1.tgz", + "integrity": "sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==", "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vitest/runner": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", - "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/signers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/signers/-/signers-5.5.1.tgz", + "integrity": "sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==", "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", - "pathe": "^2.0.3" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/offchain-messages": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vitest/snapshot": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/subscribable": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/subscribable/-/subscribable-5.5.1.tgz", + "integrity": "sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" + "@solana/errors": "5.5.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vitest/spy": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", - "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/sysvars": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/sysvars/-/sysvars-5.5.1.tgz", + "integrity": "sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==", "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" + "peer": true, + "dependencies": { + "@solana/accounts": "5.5.1", + "@solana/codecs": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@vitest/utils": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", - "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/transaction-confirmation": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transaction-confirmation/-/transaction-confirmation-5.5.1.tgz", + "integrity": "sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", - "tinyrainbow": "^3.0.3" + "@solana/addresses": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc": "5.5.1", + "@solana/rpc-subscriptions": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@whiskeysockets/baileys": { - "version": "7.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-7.0.0-rc.9.tgz", - "integrity": "sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==", - "dev": true, - "hasInstallScript": true, + "node_modules/@x402/svm/node_modules/@solana/transaction-messages": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transaction-messages/-/transaction-messages-5.5.1.tgz", + "integrity": "sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==", "license": "MIT", "dependencies": { - "@cacheable/node-cache": "^1.4.0", - "@hapi/boom": "^9.1.3", - "async-mutex": "^0.5.0", - "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", - "lru-cache": "^11.1.0", - "music-metadata": "^11.7.0", - "p-queue": "^9.0.0", - "pino": "^9.6", - "protobufjs": "^7.2.4", - "ws": "^8.13.0" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-types": "5.5.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.18.0" }, "peerDependencies": { - "audio-decode": "^2.1.3", - "jimp": "^1.6.0", - "link-preview-js": "^3.0.0", - "sharp": "*" + "typescript": "^5.0.0" }, - "peerDependenciesMeta": { - "audio-decode": { - "optional": true - }, - "jimp": { - "optional": true - }, - "link-preview-js": { + "peerDependenciesMeta": { + "typescript": { "optional": true } } }, - "node_modules/@whiskeysockets/baileys/node_modules/p-queue": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", - "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", - "dev": true, + "node_modules/@x402/svm/node_modules/@solana/transactions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transactions/-/transactions-5.5.1.tgz", + "integrity": "sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==", "license": "MIT", "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^7.0.0" + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1" }, "engines": { - "node": ">=20" + "node": ">=20.18.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@whiskeysockets/baileys/node_modules/p-timeout": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", - "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", - "dev": true, + "node_modules/@x402/svm/node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "license": "MIT", "engines": { "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@x402/svm/node_modules/undici-types": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.22.0.tgz", + "integrity": "sha512-RKZvifiL60xdsIuC80UY0dq8Z7DbJUV8/l2hOVbyZAxBzEeQU4Z58+4ZzJ6WN2Lidi9KzT5EbiGX+PI/UGYuRw==", + "license": "MIT" + }, "node_modules/abitype": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", @@ -6303,6 +8313,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6320,6 +8331,12 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -6334,7 +8351,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -6371,7 +8387,6 @@ "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=14.16" }, @@ -6412,13 +8427,18 @@ "dev": true, "license": "MIT" }, + "node_modules/apg-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/apg-js/-/apg-js-4.4.0.tgz", + "integrity": "sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q==", + "license": "BSD-2-Clause" + }, "node_modules/aproba": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/are-we-there-yet": { "version": "3.0.1", @@ -6427,7 +8447,6 @@ "deprecated": "This package is no longer supported.", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -6482,7 +8501,6 @@ "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "retry": "0.13.1" } @@ -6559,8 +8577,7 @@ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/bignumber.js": { "version": "9.3.1", @@ -6758,7 +8775,6 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -6772,8 +8788,7 @@ "resolved": "https://registry.npmjs.org/chmodrp/-/chmodrp-1.0.2.tgz", "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/chokidar": { "version": "5.0.0", @@ -6797,7 +8812,6 @@ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -6814,7 +8828,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -6825,7 +8838,6 @@ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "restore-cursor": "^5.0.0" }, @@ -6979,7 +8991,6 @@ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -7065,7 +9076,6 @@ "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "axios": "^1.6.5", "debug": "^4", @@ -7092,8 +9102,7 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/cmake-js/node_modules/which": { "version": "2.0.2", @@ -7101,7 +9110,6 @@ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -7138,7 +9146,6 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true, "license": "ISC", - "peer": true, "bin": { "color-support": "bin.js" } @@ -7162,7 +9169,6 @@ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=14" } @@ -7196,8 +9202,7 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/content-disposition": { "version": "1.0.1", @@ -7386,7 +9391,6 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4.0.0" } @@ -7428,8 +9432,7 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", @@ -7618,7 +9621,6 @@ "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -7686,6 +9688,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -7779,6 +9782,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8052,6 +10056,106 @@ "node": ">= 0.6" } }, + "node_modules/ethers": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", + "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -8172,14 +10276,12 @@ "url": "https://opencollective.com/fastify" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -8200,7 +10302,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, "funding": [ { "type": "github", @@ -8312,7 +10413,6 @@ "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -8326,7 +10426,6 @@ "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "filename-reserved-regex": "^3.0.0" }, @@ -8526,7 +10625,6 @@ "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8542,7 +10640,6 @@ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -8556,7 +10653,6 @@ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -8596,7 +10692,6 @@ "deprecated": "This package is no longer supported.", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -8617,7 +10712,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -8628,7 +10722,6 @@ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8872,6 +10965,7 @@ "integrity": "sha512-7arRRoOtOh9UwMwANZ475kJrWV6P3/EGNooeHlY0/SwZv4t3ZZ3Uiz9cAXK8Zg9xSdgmm8T21kx6n7SZaWvOcw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@grammyjs/types": "3.23.0", "abort-controller": "^3.0.0", @@ -8961,8 +11055,7 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/hashery": { "version": "1.4.0", @@ -9006,6 +11099,7 @@ "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -9210,8 +11304,7 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/ip-address": { "version": "10.1.0", @@ -9239,7 +11332,6 @@ "integrity": "sha512-ZMkxaopfwKHwmEuGDYx7giNBdLxbHbRCWcQVA1D2eqE4crUguupfxej6s7UqbidYEwT69dkyumYkY8DPHIxF9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@tinyhttp/content-disposition": "^2.2.0", "async-retry": "^1.3.3", @@ -9280,8 +11372,7 @@ "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", "integrity": "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ipull/node_modules/parse-ms": { "version": "3.0.0", @@ -9289,7 +11380,6 @@ "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9303,7 +11393,6 @@ "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "parse-ms": "^3.0.0" }, @@ -9337,7 +11426,6 @@ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "get-east-asian-width": "^1.3.1" }, @@ -9367,7 +11455,6 @@ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9401,7 +11488,6 @@ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -9422,7 +11508,6 @@ "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=16" } @@ -9526,7 +11611,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { @@ -9555,7 +11639,6 @@ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -9661,6 +11744,7 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -9746,8 +11830,7 @@ "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-3.1.0.tgz", "integrity": "sha512-kVvegv+r/icjIo1dkHv1hznVQi4FzEVglJD2IU4w07HzevIyH3BAYsFZzEIbBk/nNZjXHGgclJ5g9rz9QdBCLw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lilconfig": { "version": "3.1.3", @@ -9835,8 +11918,7 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", @@ -9900,7 +11982,6 @@ "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" @@ -9925,7 +12006,6 @@ "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "steno": "^4.0.2" }, @@ -10020,7 +12100,6 @@ "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readable-stream": "^3.4.0" } @@ -10067,7 +12146,6 @@ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -10097,7 +12175,6 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10118,7 +12195,6 @@ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -10133,7 +12209,6 @@ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -10147,7 +12222,6 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -10231,7 +12305,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.js" }, @@ -10272,7 +12345,6 @@ "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18 || ^20 || >= 21" } @@ -10282,8 +12354,7 @@ "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/node-domexception": { "version": "1.0.0", @@ -10429,7 +12500,6 @@ "deprecated": "This package is no longer supported.", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -10493,7 +12563,6 @@ "integrity": "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/app": "^16.1.2", "@octokit/core": "^7.0.6", @@ -10550,7 +12619,6 @@ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mimic-function": "^5.0.0" }, @@ -10748,7 +12816,6 @@ "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", @@ -10772,8 +12839,7 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ora/node_modules/log-symbols": { "version": "6.0.0", @@ -10781,7 +12847,6 @@ "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" @@ -10799,7 +12864,6 @@ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10813,7 +12877,6 @@ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -11032,7 +13095,6 @@ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -11163,6 +13225,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11265,6 +13328,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11368,7 +13432,6 @@ "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.13.1 || >=16.0.0" }, @@ -11382,7 +13445,6 @@ "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "parse-ms": "^4.0.0" }, @@ -11547,7 +13609,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -11640,7 +13701,6 @@ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "peer": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -11657,7 +13717,6 @@ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11705,7 +13764,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11737,7 +13795,6 @@ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" @@ -11755,7 +13812,6 @@ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=14" }, @@ -12058,8 +14114,7 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/setimmediate": { "version": "1.0.5", @@ -12082,6 +14137,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -12257,7 +14313,6 @@ "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -12275,13 +14330,27 @@ "dev": true, "license": "MIT" }, + "node_modules/siwe": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/siwe/-/siwe-2.3.2.tgz", + "integrity": "sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA==", + "license": "Apache-2.0", + "dependencies": { + "@spruceid/siwe-parser": "^2.1.2", + "@stablelib/random": "^1.0.1", + "uri-js": "^4.4.1", + "valid-url": "^1.0.9" + }, + "peerDependencies": { + "ethers": "^5.6.8 || ^6.0.8" + } + }, "node_modules/sleep-promise": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz", "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/slice-ansi": { "version": "7.1.2", @@ -12289,7 +14358,6 @@ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" @@ -12507,7 +14575,6 @@ "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12521,7 +14588,6 @@ "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-escapes": "^6.2.0", "ansi-styles": "^6.2.1", @@ -12537,8 +14603,7 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/stdout-update/node_modules/string-width": { "version": "7.2.0", @@ -12546,7 +14611,6 @@ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -12565,7 +14629,6 @@ "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12579,7 +14642,6 @@ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -12727,7 +14789,6 @@ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -12815,7 +14876,6 @@ "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -12834,7 +14894,6 @@ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=8" } @@ -12919,7 +14978,6 @@ "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" } @@ -13126,6 +15184,7 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -13140,6 +15199,12 @@ "fsevents": "~2.3.3" } }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -13201,6 +15266,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13289,16 +15355,14 @@ "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/universalify": { "version": "2.0.1", @@ -13306,7 +15370,6 @@ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 10.0.0" } @@ -13325,7 +15388,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -13336,8 +15398,7 @@ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -13346,13 +15407,17 @@ "dev": true, "license": "MIT" }, + "node_modules/valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" + }, "node_modules/validate-npm-package-name": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", "dev": true, "license": "ISC", - "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -13424,6 +15489,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -13615,7 +15681,6 @@ "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "isexe": "^3.1.1" }, @@ -13649,7 +15714,6 @@ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -13784,6 +15848,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -13815,8 +15880,7 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/yaml": { "version": "2.8.2", @@ -13893,8 +15957,8 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "devOptional": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index ceac0d8..056eeff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blockrun/clawrouter", - "version": "0.10.10", + "version": "0.11.0", "description": "Smart LLM router — save 92% on inference costs. 41+ models, one wallet, x402 micropayments.", "type": "module", "main": "dist/index.js", @@ -68,6 +68,12 @@ "url": "git+https://github.com/BlockRunAI/ClawRouter.git" }, "dependencies": { + "@scure/bip32": "^1.6.0", + "@scure/bip39": "^1.5.0", + "@solana/kit": "^6.0.0", + "@x402/evm": "^2.4.0", + "@x402/fetch": "^2.4.0", + "@x402/svm": "^2.4.0", "viem": "^2.39.3" }, "peerDependencies": { diff --git a/src/auth.ts b/src/auth.ts index 8ac9eae..7aad13b 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -27,14 +27,22 @@ import { writeFile, mkdir } from "node:fs/promises"; import { readTextFile } from "./fs-read.js"; import { join } from "node:path"; import { homedir } from "node:os"; -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { privateKeyToAccount } from "viem/accounts"; import type { ProviderAuthMethod, ProviderAuthContext, ProviderAuthResult } from "./types.js"; +import { + generateWalletMnemonic, + isValidMnemonic, + deriveEvmKey, + deriveSolanaKeyBytes, + deriveAllKeys, +} from "./wallet.js"; const WALLET_DIR = join(homedir(), ".openclaw", "blockrun"); const WALLET_FILE = join(WALLET_DIR, "wallet.key"); +const MNEMONIC_FILE = join(WALLET_DIR, "mnemonic"); -// Export for use by wallet command -export { WALLET_FILE }; +// Export for use by wallet command and index.ts +export { WALLET_FILE, MNEMONIC_FILE }; /** * Try to load a previously auto-generated wallet key from disk. @@ -78,26 +86,70 @@ async function loadSavedWallet(): Promise { } /** - * Generate a new wallet, save to disk, return the private key. + * Load mnemonic from disk if it exists. + */ +async function loadMnemonic(): Promise { + try { + const mnemonic = (await readTextFile(MNEMONIC_FILE)).trim(); + if (mnemonic && isValidMnemonic(mnemonic)) { + return mnemonic; + } + } catch { + // No mnemonic file or invalid - that's fine + } + return undefined; +} + +/** + * Save mnemonic to disk. + */ +async function saveMnemonic(mnemonic: string): Promise { + await mkdir(WALLET_DIR, { recursive: true }); + await writeFile(MNEMONIC_FILE, mnemonic + "\n", { mode: 0o600 }); +} + +/** + * Generate a new wallet with BIP-39 mnemonic, save to disk. + * New users get both EVM and Solana keys derived from the same mnemonic. * CRITICAL: Verifies the file was actually written after generation. */ -async function generateAndSaveWallet(): Promise<{ key: string; address: string }> { - const key = generatePrivateKey(); - const account = privateKeyToAccount(key); +async function generateAndSaveWallet(): Promise<{ + key: string; + address: string; + mnemonic: string; + solanaPrivateKeyBytes: Uint8Array; +}> { + // Safety: if a mnemonic file already exists, a Solana wallet was derived from it. + // Generating a new wallet would overwrite the mnemonic and lose Solana funds. + const existingMnemonic = await loadMnemonic(); + if (existingMnemonic) { + throw new Error( + `Mnemonic file exists at ${MNEMONIC_FILE} but wallet.key is missing. ` + + `This means a Solana wallet was derived from this mnemonic. ` + + `Refusing to generate a new wallet to protect Solana funds. ` + + `Restore your EVM key with: export BLOCKRUN_WALLET_KEY=`, + ); + } + + const mnemonic = generateWalletMnemonic(); + const derived = deriveAllKeys(mnemonic); // Create directory await mkdir(WALLET_DIR, { recursive: true }); - // Write wallet file - await writeFile(WALLET_FILE, key + "\n", { mode: 0o600 }); + // Write wallet key file (EVM private key) + await writeFile(WALLET_FILE, derived.evmPrivateKey + "\n", { mode: 0o600 }); + + // Write mnemonic file + await writeFile(MNEMONIC_FILE, mnemonic + "\n", { mode: 0o600 }); // CRITICAL: Verify the file was actually written try { const verification = (await readTextFile(WALLET_FILE)).trim(); - if (verification !== key) { + if (verification !== derived.evmPrivateKey) { throw new Error("Wallet file verification failed - content mismatch"); } - console.log(`[ClawRouter] āœ“ Wallet saved and verified at ${WALLET_FILE}`); + console.log(`[ClawRouter] Wallet saved and verified at ${WALLET_FILE}`); } catch (err) { throw new Error( `Failed to verify wallet file after creation: ${err instanceof Error ? err.message : String(err)}`, @@ -109,9 +161,11 @@ async function generateAndSaveWallet(): Promise<{ key: string; address: string } console.log(`[ClawRouter] ════════════════════════════════════════════════`); console.log(`[ClawRouter] NEW WALLET GENERATED — BACK UP YOUR KEY NOW`); console.log(`[ClawRouter] ════════════════════════════════════════════════`); - console.log(`[ClawRouter] Address : ${account.address}`); - console.log(`[ClawRouter] Key file: ${WALLET_FILE}`); + console.log(`[ClawRouter] EVM Address : ${derived.evmAddress}`); + console.log(`[ClawRouter] Key file : ${WALLET_FILE}`); + console.log(`[ClawRouter] Mnemonic : ${MNEMONIC_FILE}`); console.log(`[ClawRouter]`); + console.log(`[ClawRouter] Both EVM (Base) and Solana wallets are ready.`); console.log(`[ClawRouter] To back up, run in OpenClaw:`); console.log(`[ClawRouter] /wallet export`); console.log(`[ClawRouter]`); @@ -120,22 +174,46 @@ async function generateAndSaveWallet(): Promise<{ key: string; address: string } console.log(`[ClawRouter] ════════════════════════════════════════════════`); console.log(`[ClawRouter]`); - return { key, address: account.address }; + return { + key: derived.evmPrivateKey, + address: derived.evmAddress, + mnemonic, + solanaPrivateKeyBytes: derived.solanaPrivateKeyBytes, + }; } /** * Resolve wallet key: load saved → env var → auto-generate. + * Also loads mnemonic if available for Solana key derivation. * Called by index.ts before the auth wizard runs. */ -export async function resolveOrGenerateWalletKey(): Promise<{ +export type WalletResolution = { key: string; address: string; source: "saved" | "env" | "generated"; -}> { + mnemonic?: string; + solanaPrivateKeyBytes?: Uint8Array; +}; + +export async function resolveOrGenerateWalletKey(): Promise { // 1. Previously saved wallet const saved = await loadSavedWallet(); if (saved) { const account = privateKeyToAccount(saved as `0x${string}`); + + // Check if mnemonic exists (Solana support enabled) + const mnemonic = await loadMnemonic(); + if (mnemonic) { + const solanaKeyBytes = deriveSolanaKeyBytes(mnemonic); + return { + key: saved, + address: account.address, + source: "saved", + mnemonic, + solanaPrivateKeyBytes: solanaKeyBytes, + }; + } + return { key: saved, address: account.address, source: "saved" }; } @@ -146,9 +224,54 @@ export async function resolveOrGenerateWalletKey(): Promise<{ return { key: envKey, address: account.address, source: "env" }; } - // 3. Auto-generate - const { key, address } = await generateAndSaveWallet(); - return { key, address, source: "generated" }; + // 3. Auto-generate with BIP-39 mnemonic (new users get both chains) + const result = await generateAndSaveWallet(); + return { + key: result.key, + address: result.address, + source: "generated", + mnemonic: result.mnemonic, + solanaPrivateKeyBytes: result.solanaPrivateKeyBytes, + }; +} + +/** + * Set up Solana wallet for existing EVM-only users. + * Generates a new mnemonic for Solana key derivation. + * NEVER touches the existing wallet.key file. + */ +export async function setupSolana(): Promise<{ + mnemonic: string; + solanaPrivateKeyBytes: Uint8Array; +}> { + // Safety: mnemonic must not already exist + const existing = await loadMnemonic(); + if (existing) { + throw new Error( + "Solana wallet already set up. Mnemonic file exists at " + MNEMONIC_FILE, + ); + } + + // Safety: wallet.key must exist (can't set up Solana without EVM wallet) + const savedKey = await loadSavedWallet(); + if (!savedKey) { + throw new Error( + "No EVM wallet found. Run ClawRouter first to generate a wallet before setting up Solana.", + ); + } + + // Generate new mnemonic for Solana derivation + const mnemonic = generateWalletMnemonic(); + const solanaKeyBytes = deriveSolanaKeyBytes(mnemonic); + + // Save mnemonic (wallet.key untouched) + await saveMnemonic(mnemonic); + + console.log(`[ClawRouter] Solana wallet set up successfully.`); + console.log(`[ClawRouter] Mnemonic saved to ${MNEMONIC_FILE}`); + console.log(`[ClawRouter] Existing EVM wallet unchanged.`); + + return { mnemonic, solanaPrivateKeyBytes: solanaKeyBytes }; } /** diff --git a/src/cli.ts b/src/cli.ts index 1d0a1e2..7465e4d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -179,19 +179,19 @@ async function main(): Promise { } // Resolve wallet key - const { key: walletKey, address, source } = await resolveOrGenerateWalletKey(); + const wallet = await resolveOrGenerateWalletKey(); - if (source === "generated") { - console.log(`[ClawRouter] Generated new wallet: ${address}`); - } else if (source === "saved") { - console.log(`[ClawRouter] Using saved wallet: ${address}`); + if (wallet.source === "generated") { + console.log(`[ClawRouter] Generated new wallet: ${wallet.address}`); + } else if (wallet.source === "saved") { + console.log(`[ClawRouter] Using saved wallet: ${wallet.address}`); } else { - console.log(`[ClawRouter] Using wallet from BLOCKRUN_WALLET_KEY: ${address}`); + console.log(`[ClawRouter] Using wallet from BLOCKRUN_WALLET_KEY: ${wallet.address}`); } // Start the proxy const proxy = await startProxy({ - walletKey, + wallet, port: args.port, onReady: (port) => { console.log(`[ClawRouter] Proxy listening on http://127.0.0.1:${port}`); @@ -217,19 +217,19 @@ async function main(): Promise { }); // Check balance - const monitor = new BalanceMonitor(address); + const monitor = new BalanceMonitor(wallet.address); try { const balance = await monitor.checkBalance(); if (balance.isEmpty) { console.log(`[ClawRouter] Wallet balance: $0.00 (using FREE model)`); - console.log(`[ClawRouter] Fund wallet for premium models: ${address}`); + console.log(`[ClawRouter] Fund wallet for premium models: ${wallet.address}`); } else if (balance.isLow) { console.log(`[ClawRouter] Wallet balance: ${balance.balanceUSD} (low)`); } else { console.log(`[ClawRouter] Wallet balance: ${balance.balanceUSD}`); } } catch { - console.log(`[ClawRouter] Wallet: ${address} (balance check pending)`); + console.log(`[ClawRouter] Wallet: ${wallet.address} (balance check pending)`); } console.log(`[ClawRouter] Ready - Ctrl+C to stop`); diff --git a/src/doctor.ts b/src/doctor.ts index a9223a0..9a32abf 100644 --- a/src/doctor.ts +++ b/src/doctor.ts @@ -8,10 +8,15 @@ import { homedir, platform, arch, freemem, totalmem } from "node:os"; import { join } from "node:path"; import { stat, readdir } from "node:fs/promises"; +import { createPublicClient, http } from "viem"; +import { base } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; +import { wrapFetchWithPayment, x402Client } from "@x402/fetch"; +import { registerExactEvmScheme } from "@x402/evm/exact/client"; +import { toClientEvmSigner } from "@x402/evm"; import { resolveOrGenerateWalletKey, WALLET_FILE } from "./auth.js"; import { BalanceMonitor } from "./balance.js"; import { getStats } from "./stats.js"; -import { createPaymentFetch } from "./x402.js"; import { getProxyPort } from "./proxy.js"; import { VERSION } from "./version.js"; @@ -314,7 +319,12 @@ async function analyzeWithAI( try { const { key } = await resolveOrGenerateWalletKey(); - const { fetch: paymentFetch } = createPaymentFetch(key as `0x${string}`); + const account = privateKeyToAccount(key as `0x${string}`); + const publicClient = createPublicClient({ chain: base, transport: http() }); + const evmSigner = toClientEvmSigner(account, publicClient); + const x402 = new x402Client(); + registerExactEvmScheme(x402, { signer: evmSigner }); + const paymentFetch = wrapFetchWithPayment(fetch, x402); const response = await paymentFetch( "https://blockrun.ai/api/v1/chat/completions", @@ -345,7 +355,6 @@ Analyze the diagnostics and: max_tokens: 1000, }), }, - undefined, ); if (!response.ok) { diff --git a/src/index.ts b/src/index.ts index 7ac29ba..ec70331 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,7 +25,7 @@ import type { } from "./types.js"; import { blockrunProvider, setActiveProxy } from "./provider.js"; import { startProxy, getProxyPort } from "./proxy.js"; -import { resolveOrGenerateWalletKey, WALLET_FILE } from "./auth.js"; +import { resolveOrGenerateWalletKey, setupSolana, WALLET_FILE, MNEMONIC_FILE } from "./auth.js"; import type { RoutingConfig } from "./router/index.js"; import { BalanceMonitor } from "./balance.js"; @@ -428,27 +428,27 @@ let activeProxyHandle: Awaited> | null = null; */ async function startProxyInBackground(api: OpenClawPluginApi): Promise { // Resolve wallet key: saved file → env var → auto-generate - const { key: walletKey, address, source } = await resolveOrGenerateWalletKey(); + const wallet = await resolveOrGenerateWalletKey(); // Log wallet source - if (source === "generated") { + if (wallet.source === "generated") { api.logger.warn(`════════════════════════════════════════════════`); api.logger.warn(` NEW WALLET GENERATED — BACK UP YOUR KEY NOW!`); - api.logger.warn(` Address : ${address}`); + api.logger.warn(` Address : ${wallet.address}`); api.logger.warn(` Run /wallet export to get your private key`); api.logger.warn(` Losing this key = losing your USDC funds`); api.logger.warn(`════════════════════════════════════════════════`); - } else if (source === "saved") { - api.logger.info(`Using saved wallet: ${address}`); + } else if (wallet.source === "saved") { + api.logger.info(`Using saved wallet: ${wallet.address}`); } else { - api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address}`); + api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${wallet.address}`); } // Resolve routing config overrides from plugin config const routingConfig = api.pluginConfig?.routing as Partial | undefined; const proxy = await startProxy({ - walletKey, + wallet, routingConfig, onReady: (port) => { api.logger.info(`BlockRun x402 proxy listening on port ${port}`); @@ -480,22 +480,22 @@ async function startProxyInBackground(api: OpenClawPluginApi): Promise { api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`); // Non-blocking balance check AFTER proxy is ready (won't hang startup) - const startupMonitor = new BalanceMonitor(address); + const startupMonitor = new BalanceMonitor(wallet.address); startupMonitor .checkBalance() .then((balance) => { if (balance.isEmpty) { - api.logger.info(`Wallet: ${address} | Balance: $0.00`); + api.logger.info(`Wallet: ${wallet.address} | Balance: $0.00`); api.logger.info(`Using FREE model. Fund wallet for premium models.`); } else if (balance.isLow) { - api.logger.info(`Wallet: ${address} | Balance: ${balance.balanceUSD} (low)`); + api.logger.info(`Wallet: ${wallet.address} | Balance: ${balance.balanceUSD} (low)`); } else { - api.logger.info(`Wallet: ${address} | Balance: ${balance.balanceUSD}`); + api.logger.info(`Wallet: ${wallet.address} | Balance: ${balance.balanceUSD}`); } }) .catch(() => { // Silently continue - balance will be checked per-request anyway - api.logger.info(`Wallet: ${address} | Balance: (checking...)`); + api.logger.info(`Wallet: ${wallet.address} | Balance: (checking...)`); }); } @@ -567,52 +567,157 @@ async function createWalletCommand(): Promise { } if (subcommand === "export") { - // Export private key for backup - return { - text: [ - "šŸ” **ClawRouter Wallet Export**", - "", - "āš ļø **SECURITY WARNING**: Your private key controls your wallet funds.", - "Never share this key. Anyone with this key can spend your USDC.", - "", - `**Address:** \`${address}\``, - "", - `**Private Key:**`, - `\`${walletKey}\``, - "", - "**To restore on a new machine:**", - "1. Set the environment variable before running OpenClaw:", - ` \`export BLOCKRUN_WALLET_KEY=${walletKey}\``, - "2. Or save to file:", - ` \`mkdir -p ~/.openclaw/blockrun && echo "${walletKey}" > ~/.openclaw/blockrun/wallet.key && chmod 600 ~/.openclaw/blockrun/wallet.key\``, - ].join("\n"), - }; + // Export private key + mnemonic for backup + const lines = [ + "**ClawRouter Wallet Export**", + "", + "**SECURITY WARNING**: Your private key and mnemonic control your wallet funds.", + "Never share these. Anyone with them can spend your USDC.", + "", + "**EVM (Base):**", + ` Address: \`${address}\``, + ` Private Key: \`${walletKey}\``, + ]; + + // Include mnemonic if it exists (Solana wallet derived from it) + let hasMnemonic = false; + try { + if (existsSync(MNEMONIC_FILE)) { + const mnemonic = readTextFileSync(MNEMONIC_FILE).trim(); + if (mnemonic) { + hasMnemonic = true; + // Derive Solana address for display + const { deriveSolanaKeyBytes } = await import("./wallet.js"); + const solKeyBytes = deriveSolanaKeyBytes(mnemonic); + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const signer = await createKeyPairSignerFromPrivateKeyBytes(solKeyBytes); + + lines.push( + "", + "**Solana:**", + ` Address: \`${signer.address}\``, + ` (Derived from mnemonic below)`, + "", + "**Mnemonic (24 words):**", + `\`${mnemonic}\``, + "", + "CRITICAL: Back up this mnemonic. It is the ONLY way to recover your Solana wallet.", + ); + } + } + } catch { + // No mnemonic - EVM-only wallet + } + + lines.push( + "", + "**To restore on a new machine:**", + "1. Set the environment variable before running OpenClaw:", + ` \`export BLOCKRUN_WALLET_KEY=${walletKey}\``, + "2. Or save to file:", + ` \`mkdir -p ~/.openclaw/blockrun && echo "${walletKey}" > ~/.openclaw/blockrun/wallet.key && chmod 600 ~/.openclaw/blockrun/wallet.key\``, + ); + + if (hasMnemonic) { + lines.push( + "3. Restore the mnemonic for Solana:", + ` \`echo "" > ~/.openclaw/blockrun/mnemonic && chmod 600 ~/.openclaw/blockrun/mnemonic\``, + ); + } + + return { text: lines.join("\n") }; + } + + if (subcommand === "setup-solana") { + // Set up Solana wallet for existing EVM-only users + try { + const { solanaPrivateKeyBytes } = await setupSolana(); + // Derive Solana address for display + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const signer = await createKeyPairSignerFromPrivateKeyBytes(solanaPrivateKeyBytes); + return { + text: [ + "**Solana Wallet Set Up**", + "", + `**Solana Address:** \`${signer.address}\``, + `**Mnemonic File:** \`${MNEMONIC_FILE}\``, + "", + "Your existing EVM wallet is unchanged.", + "Restart the gateway to enable Solana payments.", + "", + `**Fund with USDC on Solana:** https://solscan.io/account/${signer.address}`, + ].join("\n"), + }; + } catch (err) { + return { + text: `Failed to set up Solana: ${err instanceof Error ? err.message : String(err)}`, + isError: true, + }; + } } // Default: show wallet status - let balanceText = "Balance: (checking...)"; + let evmBalanceText = "Balance: (checking...)"; try { const monitor = new BalanceMonitor(address); const balance = await monitor.checkBalance(); - balanceText = `Balance: ${balance.balanceUSD}`; + evmBalanceText = `Balance: ${balance.balanceUSD}`; } catch { - balanceText = "Balance: (could not check)"; + evmBalanceText = "Balance: (could not check)"; + } + + // Check for Solana wallet + let solanaSection = ""; + try { + if (existsSync(MNEMONIC_FILE)) { + const { deriveSolanaKeyBytes } = await import("./wallet.js"); + const mnemonic = readTextFileSync(MNEMONIC_FILE).trim(); + if (mnemonic) { + const solKeyBytes = deriveSolanaKeyBytes(mnemonic); + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const signer = await createKeyPairSignerFromPrivateKeyBytes(solKeyBytes); + const solAddr = signer.address; + + let solBalanceText = "Balance: (checking...)"; + try { + const { SolanaBalanceMonitor } = await import("./solana-balance.js"); + const solMonitor = new SolanaBalanceMonitor(solAddr); + const solBalance = await solMonitor.checkBalance(); + solBalanceText = `Balance: ${solBalance.balanceUSD}`; + } catch { + solBalanceText = "Balance: (could not check)"; + } + + solanaSection = [ + "", + "**Solana:**", + ` Address: \`${solAddr}\``, + ` ${solBalanceText}`, + ` Fund: https://solscan.io/account/${solAddr}`, + ].join("\n"); + } + } + } catch { + // No Solana wallet - that's fine } return { text: [ - "šŸ¦ž **ClawRouter Wallet**", + "**ClawRouter Wallet**", + "", + "**Base (EVM):**", + ` Address: \`${address}\``, + ` ${evmBalanceText}`, + ` Fund: https://basescan.org/address/${address}`, + solanaSection, "", - `**Address:** \`${address}\``, - `**${balanceText}**`, `**Key File:** \`${WALLET_FILE}\``, "", "**Commands:**", "• `/wallet` - Show this status", "• `/wallet export` - Export private key for backup", - "", - `**Fund with USDC on Base:** https://basescan.org/address/${address}`, - ].join("\n"), + !solanaSection ? "• `/wallet setup-solana` - Enable Solana payments" : "", + ].filter(Boolean).join("\n"), }; }, }; @@ -816,7 +921,8 @@ export default plugin; // Re-export for programmatic use export { startProxy, getProxyPort } from "./proxy.js"; -export type { ProxyOptions, ProxyHandle, LowBalanceInfo, InsufficientFundsInfo } from "./proxy.js"; +export type { ProxyOptions, ProxyHandle, WalletConfig, LowBalanceInfo, InsufficientFundsInfo } from "./proxy.js"; +export type { WalletResolution } from "./auth.js"; export { blockrunProvider } from "./provider.js"; export { OPENCLAW_MODELS, @@ -840,12 +946,34 @@ export { logUsage } from "./logger.js"; export type { UsageEntry } from "./logger.js"; export { RequestDeduplicator } from "./dedup.js"; export type { CachedResponse } from "./dedup.js"; -export { PaymentCache } from "./payment-cache.js"; -export type { CachedPaymentParams } from "./payment-cache.js"; -export { createPaymentFetch } from "./x402.js"; -export type { PreAuthParams, PaymentFetchResult } from "./x402.js"; export { BalanceMonitor, BALANCE_THRESHOLDS } from "./balance.js"; export type { BalanceInfo, SufficiencyResult } from "./balance.js"; +export { SolanaBalanceMonitor } from "./solana-balance.js"; +export type { SolanaBalanceInfo } from "./solana-balance.js"; +export { + SpendControl, + FileSpendControlStorage, + InMemorySpendControlStorage, + formatDuration, +} from "./spend-control.js"; +export type { + SpendWindow, + SpendLimits, + SpendRecord, + SpendingStatus, + CheckResult, + SpendControlStorage, + SpendControlOptions, +} from "./spend-control.js"; +export { + generateWalletMnemonic, + isValidMnemonic, + deriveEvmKey, + deriveSolanaKeyBytes, + deriveAllKeys, +} from "./wallet.js"; +export type { DerivedKeys } from "./wallet.js"; +export { setupSolana } from "./auth.js"; export { InsufficientFundsError, EmptyWalletError, diff --git a/src/payment-cache.ts b/src/payment-cache.ts deleted file mode 100644 index 40f3c8f..0000000 --- a/src/payment-cache.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Payment Parameter Cache - * - * Caches the 402 payment parameters (payTo, asset, network, etc.) after the first - * request to each endpoint. On subsequent requests, pre-signs the payment and - * attaches it to the first request, skipping the 402 round trip (~200ms savings). - */ - -export type CachedPaymentParams = { - payTo: string; - asset: string; - scheme: string; - network: string; - extra?: { name?: string; version?: string }; - maxTimeoutSeconds?: number; - resourceUrl?: string; - resourceDescription?: string; - cachedAt: number; -}; - -const DEFAULT_TTL_MS = 3_600_000; // 1 hour - -export class PaymentCache { - private cache = new Map(); - private ttlMs: number; - - constructor(ttlMs = DEFAULT_TTL_MS) { - this.ttlMs = ttlMs; - } - - /** Get cached payment params for an endpoint path. */ - get(endpointPath: string): CachedPaymentParams | undefined { - const entry = this.cache.get(endpointPath); - if (!entry) return undefined; - if (Date.now() - entry.cachedAt > this.ttlMs) { - this.cache.delete(endpointPath); - return undefined; - } - return entry; - } - - /** Cache payment params from a 402 response. */ - set(endpointPath: string, params: Omit): void { - this.cache.set(endpointPath, { ...params, cachedAt: Date.now() }); - } - - /** Invalidate cache for an endpoint (e.g., if payTo changed). */ - invalidate(endpointPath: string): void { - this.cache.delete(endpointPath); - } -} diff --git a/src/proxy.ts b/src/proxy.ts index a3b705e..0bc5c85 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -15,8 +15,6 @@ * before the x402 flow, preventing OpenClaw's 10-15s timeout from firing. * - Response dedup: hashes request bodies and caches responses for 30s, * preventing double-charging when OpenClaw retries after timeout. - * - Payment cache: after first 402, pre-signs subsequent requests to skip - * the 402 round trip (~200ms savings per request). * - Smart routing: when model is "blockrun/auto", classify query and pick cheapest model. * - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/ */ @@ -24,8 +22,12 @@ import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; import { finished } from "node:stream"; import type { AddressInfo } from "node:net"; +import { createPublicClient, http } from "viem"; +import { base } from "viem/chains"; import { privateKeyToAccount } from "viem/accounts"; -import { createPaymentFetch, type PreAuthParams } from "./x402.js"; +import { wrapFetchWithPayment, x402Client } from "@x402/fetch"; +import { registerExactEvmScheme } from "@x402/evm/exact/client"; +import { toClientEvmSigner } from "@x402/evm"; import { route, getFallbackChain, @@ -60,6 +62,7 @@ import { PROXY_PORT } from "./config.js"; import { SessionJournal } from "./journal.js"; const BLOCKRUN_API = "https://blockrun.ai/api"; +const BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api"; // Routing profile models - virtual models that trigger intelligent routing const AUTO_MODEL = "blockrun/auto"; @@ -811,8 +814,16 @@ export type InsufficientFundsInfo = { walletAddress: string; }; +/** + * Wallet config: either a plain EVM private key string, or the full + * resolution object from resolveOrGenerateWalletKey() which may include + * Solana keys. Using the full object prevents callers from accidentally + * forgetting to forward Solana key bytes. + */ +export type WalletConfig = string | { key: string; solanaPrivateKeyBytes?: Uint8Array }; + export type ProxyOptions = { - walletKey: string; + wallet: WalletConfig; apiBase?: string; /** Port to listen on (default: 8402) */ port?: number; @@ -859,6 +870,7 @@ export type ProxyHandle = { port: number; baseUrl: string; walletAddress: string; + solanaAddress?: string; balanceMonitor: BalanceMonitor; close: () => Promise; }; @@ -957,7 +969,6 @@ async function proxyPartnerRequest( payFetch: ( input: RequestInfo | URL, init?: RequestInit, - preAuth?: PreAuthParams, ) => Promise, ): Promise { const startTime = Date.now(); @@ -1039,7 +1050,13 @@ async function proxyPartnerRequest( * Returns a handle with the assigned port, base URL, and a close function. */ export async function startProxy(options: ProxyOptions): Promise { - const apiBase = options.apiBase ?? BLOCKRUN_API; + // Normalize wallet config: string = EVM-only, object = full resolution + const walletKey = typeof options.wallet === "string" ? options.wallet : options.wallet.key; + const solanaPrivateKeyBytes = + typeof options.wallet === "string" ? undefined : options.wallet.solanaPrivateKeyBytes; + + // Use Solana API when Solana keys are available, EVM API otherwise + const apiBase = options.apiBase ?? (solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API); // Determine port: options.port > env var > default const listenPort = options.port ?? getProxyPort(); @@ -1048,7 +1065,7 @@ export async function startProxy(options: ProxyOptions): Promise { const existingWallet = await checkExistingProxy(listenPort); if (existingWallet) { // Proxy already running — reuse it instead of failing with EADDRINUSE - const account = privateKeyToAccount(options.walletKey as `0x${string}`); + const account = privateKeyToAccount(walletKey as `0x${string}`); const balanceMonitor = new BalanceMonitor(account.address); const baseUrl = `http://127.0.0.1:${listenPort}`; @@ -1072,9 +1089,35 @@ export async function startProxy(options: ProxyOptions): Promise { }; } - // Create x402 payment-enabled fetch from wallet private key - const account = privateKeyToAccount(options.walletKey as `0x${string}`); - const { fetch: payFetch } = createPaymentFetch(options.walletKey as `0x${string}`); + // Create x402 payment client with EVM scheme (always available) + const account = privateKeyToAccount(walletKey as `0x${string}`); + const evmPublicClient = createPublicClient({ chain: base, transport: http() }); + const evmSigner = toClientEvmSigner(account, evmPublicClient); + const x402 = new x402Client(); + registerExactEvmScheme(x402, { signer: evmSigner }); + + // Register Solana scheme if key is available + // Uses registerExactSvmScheme helper which registers: + // - solana:* wildcard (catches any CAIP-2 Solana network) + // - V1 compat names: "solana", "solana-devnet", "solana-testnet" + let solanaAddress: string | undefined; + if (solanaPrivateKeyBytes) { + const { registerExactSvmScheme } = await import("@x402/svm/exact/client"); + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes(solanaPrivateKeyBytes); + solanaAddress = solanaSigner.address; + registerExactSvmScheme(x402, { signer: solanaSigner }); + console.log(`[ClawRouter] Solana x402 scheme registered: ${solanaAddress}`); + } + + // Log which chain is used for each payment + x402.onAfterPaymentCreation(async (context) => { + const network = context.selectedRequirements.network; + const chain = network.startsWith("eip155") ? "Base (EVM)" : network.startsWith("solana") ? "Solana" : network; + console.log(`[ClawRouter] Payment signed on ${chain} (${network})`); + }); + + const payFetch = wrapFetchWithPayment(fetch, x402); // Create balance monitor for pre-request checks const balanceMonitor = new BalanceMonitor(account.address); @@ -1402,6 +1445,7 @@ export async function startProxy(options: ProxyOptions): Promise { port, baseUrl, walletAddress: account.address, + solanaAddress, balanceMonitor, close: () => new Promise((res, rej) => { @@ -1450,7 +1494,6 @@ async function tryModelRequest( payFetch: ( input: RequestInfo | URL, init?: RequestInit, - preAuth?: PreAuthParams, ) => Promise, balanceMonitor: BalanceMonitor, signal: AbortSignal, @@ -1498,10 +1541,6 @@ async function tryModelRequest( // If body isn't valid JSON, use as-is } - // Estimate cost for pre-auth - const estimated = estimateAmount(modelId, requestBody.length, maxTokens); - const preAuth: PreAuthParams | undefined = estimated ? { estimatedAmount: estimated } : undefined; - try { const response = await payFetch( upstreamUrl, @@ -1511,7 +1550,6 @@ async function tryModelRequest( body: requestBody.length > 0 ? new Uint8Array(requestBody) : undefined, signal, }, - preAuth, ); // Check for provider errors @@ -1565,9 +1603,8 @@ async function tryModelRequest( * Optimizations applied in order: * 1. Dedup check — if same request body seen within 30s, replay cached response * 2. Streaming heartbeat — for stream:true, send 200 + heartbeats immediately - * 3. Payment pre-auth — estimate USDC amount and pre-sign to skip 402 round trip - * 4. Smart routing — when model is "blockrun/auto", pick cheapest capable model - * 5. Fallback chain — on provider errors, try next model in tier's fallback list + * 3. Smart routing — when model is "blockrun/auto", pick cheapest capable model + * 4. Fallback chain — on provider errors, try next model in tier's fallback list */ async function proxyRequest( req: IncomingMessage, @@ -1576,7 +1613,6 @@ async function proxyRequest( payFetch: ( input: RequestInfo | URL, init?: RequestInit, - preAuth?: PreAuthParams, ) => Promise, options: ProxyOptions, routerOpts: RouterOptions, @@ -2478,7 +2514,7 @@ async function proxyRequest( maxTokens, routingProfile ?? undefined, ); - // Apply 20% buffer to match x402 pre-auth + // Apply 20% buffer for cost estimation accuracy const costWithBuffer = accurateCosts.costEstimate * 1.2; const baselineWithBuffer = accurateCosts.baselineCost * 1.2; const entry: UsageEntry = { diff --git a/src/solana-balance.ts b/src/solana-balance.ts new file mode 100644 index 0000000..00a7955 --- /dev/null +++ b/src/solana-balance.ts @@ -0,0 +1,103 @@ +/** + * Solana USDC Balance Monitor + * + * Checks USDC balance on Solana mainnet with caching. + * Absorbed from @blockrun/clawwallet's solana-adapter.ts (balance portion only). + */ + +import { address as solAddress, createSolanaRpc } from "@solana/kit"; + +const SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"; +const SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com"; +const BALANCE_TIMEOUT_MS = 10_000; +const CACHE_TTL_MS = 30_000; + +export type SolanaBalanceInfo = { + balance: bigint; + balanceUSD: string; + isLow: boolean; + isEmpty: boolean; + walletAddress: string; +}; + +export class SolanaBalanceMonitor { + private readonly rpc: ReturnType; + private readonly walletAddress: string; + private cachedBalance: bigint | null = null; + private cachedAt = 0; + + constructor(walletAddress: string, rpcUrl?: string) { + this.walletAddress = walletAddress; + const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC; + this.rpc = createSolanaRpc(url); + } + + async checkBalance(): Promise { + const now = Date.now(); + if (this.cachedBalance !== null && now - this.cachedAt < CACHE_TTL_MS) { + return this.buildInfo(this.cachedBalance); + } + const balance = await this.fetchBalance(); + this.cachedBalance = balance; + this.cachedAt = now; + return this.buildInfo(balance); + } + + deductEstimated(amountMicros: bigint): void { + if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) { + this.cachedBalance -= amountMicros; + } + } + + invalidate(): void { + this.cachedBalance = null; + this.cachedAt = 0; + } + + async refresh(): Promise { + this.invalidate(); + return this.checkBalance(); + } + + getWalletAddress(): string { + return this.walletAddress; + } + + private async fetchBalance(): Promise { + const owner = solAddress(this.walletAddress); + const mint = solAddress(SOLANA_USDC_MINT); + + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS); + + try { + const response = await this.rpc + .getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" }) + .send({ abortSignal: controller.signal }); + + if (response.value.length === 0) return 0n; + + let total = 0n; + for (const account of response.value) { + const parsed = account.account.data as { parsed: { info: { tokenAmount: { amount: string } } } }; + total += BigInt(parsed.parsed.info.tokenAmount.amount); + } + return total; + } catch (err) { + throw new Error(`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`); + } finally { + clearTimeout(timer); + } + } + + private buildInfo(balance: bigint): SolanaBalanceInfo { + const dollars = Number(balance) / 1_000_000; + return { + balance, + balanceUSD: `$${dollars.toFixed(2)}`, + isLow: balance < 1_000_000n, + isEmpty: balance < 100n, + walletAddress: this.walletAddress, + }; + } +} diff --git a/src/spend-control.test.ts b/src/spend-control.test.ts new file mode 100644 index 0000000..3908326 --- /dev/null +++ b/src/spend-control.test.ts @@ -0,0 +1,255 @@ +/** + * SpendControl tests — limits, recording, window expiry, persistence. + */ + +import { describe, it, expect } from "vitest"; +import { + SpendControl, + InMemorySpendControlStorage, + formatDuration, +} from "./spend-control.js"; + +function createControl(nowMs = Date.now()) { + let clock = nowMs; + const storage = new InMemorySpendControlStorage(); + const control = new SpendControl({ storage, now: () => clock }); + const advance = (ms: number) => { clock += ms; }; + return { control, storage, advance }; +} + +describe("SpendControl", () => { + describe("per-request limit", () => { + it("allows requests under the limit", () => { + const { control } = createControl(); + control.setLimit("perRequest", 0.10); + expect(control.check(0.05).allowed).toBe(true); + }); + + it("blocks requests over the limit", () => { + const { control } = createControl(); + control.setLimit("perRequest", 0.10); + const result = control.check(0.15); + expect(result.allowed).toBe(false); + expect(result.blockedBy).toBe("perRequest"); + }); + + it("blocks requests exactly at the limit boundary", () => { + const { control } = createControl(); + control.setLimit("perRequest", 0.10); + // Exactly equal should pass + expect(control.check(0.10).allowed).toBe(true); + // Just over should fail + expect(control.check(0.100001).allowed).toBe(false); + }); + }); + + describe("hourly limit", () => { + it("accumulates spending within the hour", () => { + const { control } = createControl(); + control.setLimit("hourly", 1.00); + + control.record(0.40); + control.record(0.40); + expect(control.check(0.25).allowed).toBe(false); + expect(control.check(0.15).allowed).toBe(true); + }); + + it("resets after the hour window passes", () => { + const { control, advance } = createControl(); + control.setLimit("hourly", 1.00); + + control.record(0.90); + expect(control.check(0.20).allowed).toBe(false); + + // Advance past the 1-hour window + advance(61 * 60 * 1000); + expect(control.check(0.20).allowed).toBe(true); + }); + + it("provides resetIn seconds", () => { + const { control } = createControl(); + control.setLimit("hourly", 0.50); + + control.record(0.50); + const result = control.check(0.01); + expect(result.allowed).toBe(false); + expect(result.resetIn).toBeGreaterThan(0); + expect(result.resetIn).toBeLessThanOrEqual(3600); + }); + }); + + describe("daily limit", () => { + it("accumulates across hours within the day", () => { + const { control, advance } = createControl(); + control.setLimit("daily", 5.00); + + control.record(2.00); + advance(2 * 60 * 60 * 1000); // 2 hours later + control.record(2.00); + expect(control.check(1.50).allowed).toBe(false); + expect(control.check(0.90).allowed).toBe(true); + }); + + it("resets after the day window passes", () => { + const { control, advance } = createControl(); + control.setLimit("daily", 5.00); + + control.record(4.90); + expect(control.check(0.20).allowed).toBe(false); + + advance(25 * 60 * 60 * 1000); // 25 hours + expect(control.check(0.20).allowed).toBe(true); + }); + }); + + describe("session limit", () => { + it("tracks spending within the session", () => { + const { control } = createControl(); + control.setLimit("session", 2.00); + + control.record(1.50); + expect(control.check(0.60).allowed).toBe(false); + expect(control.check(0.40).allowed).toBe(true); + }); + + it("resetSession clears session spending", () => { + const { control } = createControl(); + control.setLimit("session", 2.00); + + control.record(1.90); + expect(control.check(0.20).allowed).toBe(false); + + control.resetSession(); + expect(control.check(0.20).allowed).toBe(true); + }); + }); + + describe("multiple limits", () => { + it("checks all limits and reports the first violation", () => { + const { control } = createControl(); + control.setLimit("perRequest", 0.50); + control.setLimit("hourly", 2.00); + + // Over per-request limit + const result = control.check(0.60); + expect(result.allowed).toBe(false); + expect(result.blockedBy).toBe("perRequest"); + }); + + it("checks hourly after per-request passes", () => { + const { control } = createControl(); + control.setLimit("perRequest", 1.00); + control.setLimit("hourly", 2.00); + + control.record(1.80); + const result = control.check(0.30); + expect(result.allowed).toBe(false); + expect(result.blockedBy).toBe("hourly"); + }); + }); + + describe("getStatus", () => { + it("returns current spending and remaining amounts", () => { + const { control } = createControl(); + control.setLimit("hourly", 3.00); + control.setLimit("daily", 10.00); + + control.record(1.00); + control.record(0.50); + + const status = control.getStatus(); + expect(status.spending.hourly).toBeCloseTo(1.50); + expect(status.spending.session).toBeCloseTo(1.50); + expect(status.remaining.hourly).toBeCloseTo(1.50); + expect(status.remaining.daily).toBeCloseTo(8.50); + expect(status.calls).toBe(2); + }); + }); + + describe("getHistory", () => { + it("returns records in reverse chronological order", () => { + const { control, advance } = createControl(); + control.record(0.10, { model: "first" }); + advance(1000); + control.record(0.20, { model: "second" }); + + const history = control.getHistory(); + expect(history).toHaveLength(2); + expect(history[0].model).toBe("second"); + expect(history[1].model).toBe("first"); + }); + + it("respects limit parameter", () => { + const { control, advance } = createControl(); + control.record(0.10); + advance(100); + control.record(0.20); + advance(100); + control.record(0.30); + + expect(control.getHistory(2)).toHaveLength(2); + }); + }); + + describe("clearLimit", () => { + it("removes a specific limit", () => { + const { control } = createControl(); + control.setLimit("perRequest", 0.01); + expect(control.check(0.05).allowed).toBe(false); + + control.clearLimit("perRequest"); + expect(control.check(0.05).allowed).toBe(true); + }); + }); + + describe("persistence", () => { + it("persists limits and history across instances via shared storage", () => { + const storage = new InMemorySpendControlStorage(); + let clock = Date.now(); + + const c1 = new SpendControl({ storage, now: () => clock }); + c1.setLimit("hourly", 5.00); + c1.record(2.00); + + // New instance, same storage + const c2 = new SpendControl({ storage, now: () => clock }); + expect(c2.getLimits().hourly).toBe(5.00); + expect(c2.getSpending("hourly")).toBeCloseTo(2.00); + }); + }); + + describe("validation", () => { + it("rejects non-positive limits", () => { + const { control } = createControl(); + expect(() => control.setLimit("hourly", 0)).toThrow(); + expect(() => control.setLimit("hourly", -1)).toThrow(); + }); + + it("rejects negative record amounts", () => { + const { control } = createControl(); + expect(() => control.record(-0.50)).toThrow(); + }); + + it("rejects non-finite values", () => { + const { control } = createControl(); + expect(() => control.setLimit("hourly", Infinity)).toThrow(); + expect(() => control.setLimit("hourly", NaN)).toThrow(); + }); + }); +}); + +describe("formatDuration", () => { + it("formats seconds", () => { + expect(formatDuration(30)).toBe("30s"); + }); + + it("formats minutes", () => { + expect(formatDuration(120)).toBe("2 min"); + expect(formatDuration(90)).toBe("2 min"); + }); + + it("formats hours and minutes", () => { + expect(formatDuration(3660)).toBe("1h 1m"); + expect(formatDuration(7200)).toBe("2h"); + }); +}); diff --git a/src/spend-control.ts b/src/spend-control.ts new file mode 100644 index 0000000..3ba2a30 --- /dev/null +++ b/src/spend-control.ts @@ -0,0 +1,360 @@ +/** + * Spend Control - Time-windowed spending limits + * + * Absorbed from @blockrun/clawwallet. Chain-agnostic (works for both EVM and Solana). + * + * Features: + * - Per-request limits (e.g., max $0.10 per call) + * - Hourly limits (e.g., max $3.00 per hour) + * - Daily limits (e.g., max $20.00 per day) + * - Session limits (e.g., max $5.00 per session) + * - Rolling windows (last 1h, last 24h) + * - Persistent storage (~/.openclaw/blockrun/spending.json) + */ + +import * as fs from "node:fs"; +import * as path from "node:path"; +import { homedir } from "node:os"; + +const WALLET_DIR = path.join(homedir(), ".openclaw", "blockrun"); + +const HOUR_MS = 60 * 60 * 1000; +const DAY_MS = 24 * HOUR_MS; + +export type SpendWindow = "perRequest" | "hourly" | "daily" | "session"; + +export interface SpendLimits { + perRequest?: number; + hourly?: number; + daily?: number; + session?: number; +} + +export interface SpendRecord { + timestamp: number; + amount: number; + model?: string; + action?: string; +} + +export interface SpendingStatus { + limits: SpendLimits; + spending: { + hourly: number; + daily: number; + session: number; + }; + remaining: { + hourly: number | null; + daily: number | null; + session: number | null; + }; + calls: number; +} + +export interface CheckResult { + allowed: boolean; + blockedBy?: SpendWindow; + remaining?: number; + reason?: string; + resetIn?: number; +} + +export interface SpendControlStorage { + load(): { limits: SpendLimits; history: SpendRecord[] } | null; + save(data: { limits: SpendLimits; history: SpendRecord[] }): void; +} + +export class FileSpendControlStorage implements SpendControlStorage { + private readonly spendingFile: string; + + constructor() { + this.spendingFile = path.join(WALLET_DIR, "spending.json"); + } + + load(): { limits: SpendLimits; history: SpendRecord[] } | null { + try { + if (fs.existsSync(this.spendingFile)) { + const data = JSON.parse(fs.readFileSync(this.spendingFile, "utf-8")); + const rawLimits = data.limits ?? {}; + const rawHistory = data.history ?? []; + + const limits: SpendLimits = {}; + for (const key of ["perRequest", "hourly", "daily", "session"] as const) { + const val = rawLimits[key]; + if (typeof val === "number" && val > 0 && Number.isFinite(val)) { + limits[key] = val; + } + } + + const history: SpendRecord[] = []; + if (Array.isArray(rawHistory)) { + for (const r of rawHistory) { + if ( + typeof r?.timestamp === "number" && + typeof r?.amount === "number" && + Number.isFinite(r.timestamp) && + Number.isFinite(r.amount) && + r.amount >= 0 + ) { + history.push({ + timestamp: r.timestamp, + amount: r.amount, + model: typeof r.model === "string" ? r.model : undefined, + action: typeof r.action === "string" ? r.action : undefined, + }); + } + } + } + + return { limits, history }; + } + } catch (err) { + console.error(`[ClawRouter] Failed to load spending data, starting fresh: ${err}`); + } + return null; + } + + save(data: { limits: SpendLimits; history: SpendRecord[] }): void { + try { + if (!fs.existsSync(WALLET_DIR)) { + fs.mkdirSync(WALLET_DIR, { recursive: true, mode: 0o700 }); + } + fs.writeFileSync(this.spendingFile, JSON.stringify(data, null, 2), { + mode: 0o600, + }); + } catch (err) { + console.error(`[ClawRouter] Failed to save spending data: ${err}`); + } + } +} + +export class InMemorySpendControlStorage implements SpendControlStorage { + private data: { limits: SpendLimits; history: SpendRecord[] } | null = null; + + load(): { limits: SpendLimits; history: SpendRecord[] } | null { + return this.data + ? { + limits: { ...this.data.limits }, + history: this.data.history.map((r) => ({ ...r })), + } + : null; + } + + save(data: { limits: SpendLimits; history: SpendRecord[] }): void { + this.data = { + limits: { ...data.limits }, + history: data.history.map((r) => ({ ...r })), + }; + } +} + +export interface SpendControlOptions { + storage?: SpendControlStorage; + now?: () => number; +} + +export class SpendControl { + private limits: SpendLimits = {}; + private history: SpendRecord[] = []; + private sessionSpent: number = 0; + private sessionCalls: number = 0; + private readonly storage: SpendControlStorage; + private readonly now: () => number; + + constructor(options?: SpendControlOptions) { + this.storage = options?.storage ?? new FileSpendControlStorage(); + this.now = options?.now ?? (() => Date.now()); + this.load(); + } + + setLimit(window: SpendWindow, amount: number): void { + if (!Number.isFinite(amount) || amount <= 0) { + throw new Error("Limit must be a finite positive number"); + } + this.limits[window] = amount; + this.save(); + } + + clearLimit(window: SpendWindow): void { + delete this.limits[window]; + this.save(); + } + + getLimits(): SpendLimits { + return { ...this.limits }; + } + + check(estimatedCost: number): CheckResult { + const now = this.now(); + + if (this.limits.perRequest !== undefined) { + if (estimatedCost > this.limits.perRequest) { + return { + allowed: false, + blockedBy: "perRequest", + remaining: this.limits.perRequest, + reason: `Per-request limit exceeded: $${estimatedCost.toFixed(4)} > $${this.limits.perRequest.toFixed(2)} max`, + }; + } + } + + if (this.limits.hourly !== undefined) { + const hourlySpent = this.getSpendingInWindow(now - HOUR_MS, now); + const remaining = this.limits.hourly - hourlySpent; + if (estimatedCost > remaining) { + const oldestInWindow = this.history.find((r) => r.timestamp >= now - HOUR_MS); + const resetIn = oldestInWindow + ? Math.ceil((oldestInWindow.timestamp + HOUR_MS - now) / 1000) + : 0; + return { + allowed: false, + blockedBy: "hourly", + remaining, + reason: `Hourly limit exceeded: $${(hourlySpent + estimatedCost).toFixed(2)} > $${this.limits.hourly.toFixed(2)} max`, + resetIn, + }; + } + } + + if (this.limits.daily !== undefined) { + const dailySpent = this.getSpendingInWindow(now - DAY_MS, now); + const remaining = this.limits.daily - dailySpent; + if (estimatedCost > remaining) { + const oldestInWindow = this.history.find((r) => r.timestamp >= now - DAY_MS); + const resetIn = oldestInWindow + ? Math.ceil((oldestInWindow.timestamp + DAY_MS - now) / 1000) + : 0; + return { + allowed: false, + blockedBy: "daily", + remaining, + reason: `Daily limit exceeded: $${(dailySpent + estimatedCost).toFixed(2)} > $${this.limits.daily.toFixed(2)} max`, + resetIn, + }; + } + } + + if (this.limits.session !== undefined) { + const remaining = this.limits.session - this.sessionSpent; + if (estimatedCost > remaining) { + return { + allowed: false, + blockedBy: "session", + remaining, + reason: `Session limit exceeded: $${(this.sessionSpent + estimatedCost).toFixed(2)} > $${this.limits.session.toFixed(2)} max`, + }; + } + } + + return { allowed: true }; + } + + record(amount: number, metadata?: { model?: string; action?: string }): void { + if (!Number.isFinite(amount) || amount < 0) { + throw new Error("Record amount must be a non-negative finite number"); + } + const record: SpendRecord = { + timestamp: this.now(), + amount, + model: metadata?.model, + action: metadata?.action, + }; + + this.history.push(record); + this.sessionSpent += amount; + this.sessionCalls += 1; + + this.cleanup(); + this.save(); + } + + private getSpendingInWindow(from: number, to: number): number { + return this.history + .filter((r) => r.timestamp >= from && r.timestamp <= to) + .reduce((sum, r) => sum + r.amount, 0); + } + + getSpending(window: "hourly" | "daily" | "session"): number { + const now = this.now(); + switch (window) { + case "hourly": + return this.getSpendingInWindow(now - HOUR_MS, now); + case "daily": + return this.getSpendingInWindow(now - DAY_MS, now); + case "session": + return this.sessionSpent; + } + } + + getRemaining(window: "hourly" | "daily" | "session"): number | null { + const limit = this.limits[window]; + if (limit === undefined) return null; + return Math.max(0, limit - this.getSpending(window)); + } + + getStatus(): SpendingStatus { + const now = this.now(); + const hourlySpent = this.getSpendingInWindow(now - HOUR_MS, now); + const dailySpent = this.getSpendingInWindow(now - DAY_MS, now); + + return { + limits: { ...this.limits }, + spending: { + hourly: hourlySpent, + daily: dailySpent, + session: this.sessionSpent, + }, + remaining: { + hourly: this.limits.hourly !== undefined ? this.limits.hourly - hourlySpent : null, + daily: this.limits.daily !== undefined ? this.limits.daily - dailySpent : null, + session: this.limits.session !== undefined ? this.limits.session - this.sessionSpent : null, + }, + calls: this.sessionCalls, + }; + } + + getHistory(limit?: number): SpendRecord[] { + const records = [...this.history].reverse(); + return limit ? records.slice(0, limit) : records; + } + + resetSession(): void { + this.sessionSpent = 0; + this.sessionCalls = 0; + } + + private cleanup(): void { + const cutoff = this.now() - DAY_MS; + this.history = this.history.filter((r) => r.timestamp >= cutoff); + } + + private save(): void { + this.storage.save({ + limits: { ...this.limits }, + history: [...this.history], + }); + } + + private load(): void { + const data = this.storage.load(); + if (data) { + this.limits = data.limits; + this.history = data.history; + this.cleanup(); + } + } +} + +export function formatDuration(seconds: number): string { + if (seconds < 60) { + return `${seconds}s`; + } else if (seconds < 3600) { + const mins = Math.ceil(seconds / 60); + return `${mins} min`; + } else { + const hours = Math.floor(seconds / 3600); + const mins = Math.ceil((seconds % 3600) / 60); + return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`; + } +} diff --git a/src/wallet.test.ts b/src/wallet.test.ts new file mode 100644 index 0000000..a3ef1b0 --- /dev/null +++ b/src/wallet.test.ts @@ -0,0 +1,121 @@ +/** + * Wallet derivation tests — BIP-39 mnemonic + BIP-44 key derivation. + */ + +import { describe, it, expect } from "vitest"; +import { + generateWalletMnemonic, + isValidMnemonic, + deriveEvmKey, + deriveSolanaKeyBytes, + deriveAllKeys, +} from "./wallet.js"; + +describe("wallet key derivation", () => { + const TEST_MNEMONIC = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"; + + describe("generateWalletMnemonic", () => { + it("generates a valid 24-word mnemonic", () => { + const mnemonic = generateWalletMnemonic(); + const words = mnemonic.split(" "); + expect(words).toHaveLength(24); + expect(isValidMnemonic(mnemonic)).toBe(true); + }); + + it("generates unique mnemonics each time", () => { + const a = generateWalletMnemonic(); + const b = generateWalletMnemonic(); + expect(a).not.toBe(b); + }); + }); + + describe("isValidMnemonic", () => { + it("accepts valid mnemonics", () => { + expect(isValidMnemonic(TEST_MNEMONIC)).toBe(true); + }); + + it("rejects invalid strings", () => { + expect(isValidMnemonic("not a valid mnemonic")).toBe(false); + expect(isValidMnemonic("")).toBe(false); + }); + }); + + describe("deriveEvmKey", () => { + it("derives a valid 0x-prefixed 66-char hex key", () => { + const { privateKey, address } = deriveEvmKey(TEST_MNEMONIC); + expect(privateKey).toMatch(/^0x[0-9a-f]{64}$/); + expect(address).toMatch(/^0x[0-9a-fA-F]{40}$/); + }); + + it("is deterministic for the same mnemonic", () => { + const a = deriveEvmKey(TEST_MNEMONIC); + const b = deriveEvmKey(TEST_MNEMONIC); + expect(a.privateKey).toBe(b.privateKey); + expect(a.address).toBe(b.address); + }); + + it("derives different keys for different mnemonics", () => { + const a = deriveEvmKey(TEST_MNEMONIC); + const b = deriveEvmKey(generateWalletMnemonic()); + expect(a.privateKey).not.toBe(b.privateKey); + }); + }); + + describe("deriveSolanaKeyBytes", () => { + it("returns a 32-byte Uint8Array", () => { + const bytes = deriveSolanaKeyBytes(TEST_MNEMONIC); + expect(bytes).toBeInstanceOf(Uint8Array); + expect(bytes.length).toBe(32); + }); + + it("is deterministic for the same mnemonic", () => { + const a = deriveSolanaKeyBytes(TEST_MNEMONIC); + const b = deriveSolanaKeyBytes(TEST_MNEMONIC); + expect(Buffer.from(a).toString("hex")).toBe(Buffer.from(b).toString("hex")); + }); + + it("derives different keys for different mnemonics", () => { + const a = deriveSolanaKeyBytes(TEST_MNEMONIC); + const b = deriveSolanaKeyBytes(generateWalletMnemonic()); + expect(Buffer.from(a).toString("hex")).not.toBe(Buffer.from(b).toString("hex")); + }); + }); + + describe("deriveAllKeys", () => { + it("returns EVM and Solana keys from a single mnemonic", () => { + const keys = deriveAllKeys(TEST_MNEMONIC); + expect(keys.mnemonic).toBe(TEST_MNEMONIC); + expect(keys.evmPrivateKey).toMatch(/^0x[0-9a-f]{64}$/); + expect(keys.evmAddress).toMatch(/^0x[0-9a-fA-F]{40}$/); + expect(keys.solanaPrivateKeyBytes).toBeInstanceOf(Uint8Array); + expect(keys.solanaPrivateKeyBytes.length).toBe(32); + }); + + it("EVM and Solana keys are different", () => { + const keys = deriveAllKeys(TEST_MNEMONIC); + const evmHex = keys.evmPrivateKey.slice(2); + const solHex = Buffer.from(keys.solanaPrivateKeyBytes).toString("hex"); + expect(evmHex).not.toBe(solHex); + }); + }); + + describe("Solana key produces valid signer", () => { + it("createKeyPairSignerFromPrivateKeyBytes accepts derived bytes", async () => { + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const bytes = deriveSolanaKeyBytes(TEST_MNEMONIC); + const signer = await createKeyPairSignerFromPrivateKeyBytes(bytes); + expect(typeof signer.address).toBe("string"); + expect(signer.address.length).toBeGreaterThan(20); + }); + + it("same mnemonic produces same Solana address", async () => { + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const bytesA = deriveSolanaKeyBytes(TEST_MNEMONIC); + const bytesB = deriveSolanaKeyBytes(TEST_MNEMONIC); + const signerA = await createKeyPairSignerFromPrivateKeyBytes(bytesA); + const signerB = await createKeyPairSignerFromPrivateKeyBytes(bytesB); + expect(signerA.address).toBe(signerB.address); + }); + }); +}); diff --git a/src/wallet.ts b/src/wallet.ts new file mode 100644 index 0000000..7133881 --- /dev/null +++ b/src/wallet.ts @@ -0,0 +1,70 @@ +/** + * Wallet Key Derivation + * + * BIP-39 mnemonic generation + BIP-44 HD key derivation for EVM and Solana. + * Absorbed from @blockrun/clawwallet. No file I/O here - auth.ts handles persistence. + */ + +import { HDKey } from "@scure/bip32"; +import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39"; +import { wordlist as english } from "@scure/bip39/wordlists/english"; +import { privateKeyToAccount } from "viem/accounts"; + +const ETH_DERIVATION_PATH = "m/44'/60'/0'/0/0"; +const SOLANA_DERIVATION_PATH = "m/44'/501'/0'/0'"; + +export interface DerivedKeys { + mnemonic: string; + evmPrivateKey: `0x${string}`; + evmAddress: string; + solanaPrivateKeyBytes: Uint8Array; // 32 bytes +} + +/** + * Generate a 24-word BIP-39 mnemonic. + */ +export function generateWalletMnemonic(): string { + return generateMnemonic(english, 256); +} + +/** + * Validate a BIP-39 mnemonic. + */ +export function isValidMnemonic(mnemonic: string): boolean { + return validateMnemonic(mnemonic, english); +} + +/** + * Derive EVM private key and address from a BIP-39 mnemonic. + * Path: m/44'/60'/0'/0/0 (standard Ethereum derivation) + */ +export function deriveEvmKey(mnemonic: string): { privateKey: `0x${string}`; address: string } { + const seed = mnemonicToSeedSync(mnemonic); + const hdKey = HDKey.fromMasterSeed(seed); + const derived = hdKey.derive(ETH_DERIVATION_PATH); + if (!derived.privateKey) throw new Error("Failed to derive EVM private key"); + const hex = `0x${Buffer.from(derived.privateKey).toString("hex")}` as `0x${string}`; + const account = privateKeyToAccount(hex); + return { privateKey: hex, address: account.address }; +} + +/** + * Derive 32-byte Solana private key from a BIP-39 mnemonic. + * Path: m/44'/501'/0'/0' (standard Solana derivation) + */ +export function deriveSolanaKeyBytes(mnemonic: string): Uint8Array { + const seed = mnemonicToSeedSync(mnemonic); + const hdKey = HDKey.fromMasterSeed(seed); + const derived = hdKey.derive(SOLANA_DERIVATION_PATH); + if (!derived.privateKey) throw new Error("Failed to derive Solana private key"); + return new Uint8Array(derived.privateKey); +} + +/** + * Derive both EVM and Solana keys from a single mnemonic. + */ +export function deriveAllKeys(mnemonic: string): DerivedKeys { + const { privateKey: evmPrivateKey, address: evmAddress } = deriveEvmKey(mnemonic); + const solanaPrivateKeyBytes = deriveSolanaKeyBytes(mnemonic); + return { mnemonic, evmPrivateKey, evmAddress, solanaPrivateKeyBytes }; +} diff --git a/src/x402-sdk.test.ts b/src/x402-sdk.test.ts new file mode 100644 index 0000000..89b8cbf --- /dev/null +++ b/src/x402-sdk.test.ts @@ -0,0 +1,190 @@ +/** + * x402 SDK integration tests — verifies client setup, scheme registration, + * and wrapFetchWithPayment behavior. + */ + +import { describe, it, expect, vi } from "vitest"; +import { wrapFetchWithPayment, x402Client } from "@x402/fetch"; +import { registerExactEvmScheme } from "@x402/evm/exact/client"; +import { toClientEvmSigner } from "@x402/evm"; +import { createPublicClient, http } from "viem"; +import { base } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; +import { deriveAllKeys, generateWalletMnemonic } from "./wallet.js"; + +// Deterministic test mnemonic (DO NOT use in production) +const TEST_MNEMONIC = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"; + +function createTestEvmClient(): x402Client { + const keys = deriveAllKeys(TEST_MNEMONIC); + const account = privateKeyToAccount(keys.evmPrivateKey); + const publicClient = createPublicClient({ chain: base, transport: http() }); + const signer = toClientEvmSigner(account, publicClient); + const client = new x402Client(); + registerExactEvmScheme(client, { signer }); + return client; +} + +describe("x402 SDK integration", () => { + describe("x402Client creation", () => { + it("creates a client and registers EVM scheme without error", () => { + const client = createTestEvmClient(); + expect(client).toBeInstanceOf(x402Client); + }); + + it("registers Solana scheme via registerExactSvmScheme", async () => { + const { registerExactSvmScheme } = await import("@x402/svm/exact/client"); + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + + const keys = deriveAllKeys(TEST_MNEMONIC); + const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes(keys.solanaPrivateKeyBytes); + + const client = createTestEvmClient(); + // Should not throw + registerExactSvmScheme(client, { signer: solanaSigner }); + }); + + it("registers both EVM and Solana on the same client", async () => { + const { registerExactSvmScheme } = await import("@x402/svm/exact/client"); + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + + const keys = deriveAllKeys(TEST_MNEMONIC); + const account = privateKeyToAccount(keys.evmPrivateKey); + const publicClient = createPublicClient({ chain: base, transport: http() }); + const evmSigner = toClientEvmSigner(account, publicClient); + const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes(keys.solanaPrivateKeyBytes); + + const client = new x402Client(); + registerExactEvmScheme(client, { signer: evmSigner }); + registerExactSvmScheme(client, { signer: solanaSigner }); + + // Client exists and didn't throw during dual registration + expect(client).toBeInstanceOf(x402Client); + }); + }); + + describe("wrapFetchWithPayment", () => { + it("passes through non-402 responses unchanged", async () => { + const mockResponse = new Response(JSON.stringify({ ok: true }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + const mockFetch = vi.fn().mockResolvedValue(mockResponse); + + const client = createTestEvmClient(); + const payFetch = wrapFetchWithPayment(mockFetch as unknown as typeof fetch, client); + + const res = await payFetch("https://example.com/api"); + expect(res.status).toBe(200); + expect(mockFetch).toHaveBeenCalledTimes(1); + + const body = await res.json(); + expect(body).toEqual({ ok: true }); + }); + + it("passes through streaming responses without buffering", async () => { + const encoder = new TextEncoder(); + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(encoder.encode("data: chunk1\n\n")); + controller.enqueue(encoder.encode("data: chunk2\n\n")); + controller.close(); + }, + }); + const mockResponse = new Response(stream, { + status: 200, + headers: { "Content-Type": "text/event-stream" }, + }); + const mockFetch = vi.fn().mockResolvedValue(mockResponse); + + const client = createTestEvmClient(); + const payFetch = wrapFetchWithPayment(mockFetch as unknown as typeof fetch, client); + + const res = await payFetch("https://example.com/stream"); + expect(res.status).toBe(200); + expect(res.headers.get("Content-Type")).toBe("text/event-stream"); + + // Body is still a readable stream + const text = await res.text(); + expect(text).toContain("chunk1"); + expect(text).toContain("chunk2"); + }); + + it("attempts payment on 402 response", async () => { + // First call returns 402, second (after payment) returns 200 + const mockFetch = vi.fn() + .mockResolvedValueOnce( + new Response(JSON.stringify({ error: "payment required" }), { + status: 402, + headers: { + "X-PAYMENT": JSON.stringify({ + x402Version: 1, + scheme: "exact", + network: "base", + paymentRequirements: [{ + scheme: "exact", + network: "base", + maxAmountRequired: "1000", + resource: "https://example.com/api", + description: "test", + mimeType: "application/json", + payTo: "0x0000000000000000000000000000000000000001", + maxTimeoutSeconds: 60, + asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + outputSchema: undefined, + extra: {}, + }], + }), + }, + }), + ) + .mockResolvedValueOnce( + new Response(JSON.stringify({ result: "paid" }), { status: 200 }), + ); + + const client = createTestEvmClient(); + const payFetch = wrapFetchWithPayment(mockFetch as unknown as typeof fetch, client); + + // This will get the 402, attempt to sign payment, and retry. + // The signing may fail (no real on-chain state) but the flow + // demonstrates the SDK processes 402 responses correctly. + try { + const res = await payFetch("https://example.com/api"); + // If payment signing succeeded (unlikely without chain state), + // the second response should be returned + expect(res.status).toBe(200); + } catch (err) { + // Expected: signing fails without real chain state, + // but the error should be about payment creation, not parsing + expect(err).toBeInstanceOf(Error); + expect((err as Error).message).toMatch(/payment/i); + } + + // First call always happens (gets the 402) + expect(mockFetch).toHaveBeenCalledTimes(1); + }); + + it("preserves request headers on non-402 pass-through", async () => { + const mockFetch = vi.fn().mockResolvedValue( + new Response("ok", { status: 200 }), + ); + + const client = createTestEvmClient(); + const payFetch = wrapFetchWithPayment(mockFetch as unknown as typeof fetch, client); + + await payFetch("https://example.com/api", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Custom": "test-value", + }, + body: JSON.stringify({ prompt: "hello" }), + }); + + const calledRequest = mockFetch.mock.calls[0][0] as Request; + expect(calledRequest.headers.get("Content-Type")).toBe("application/json"); + expect(calledRequest.headers.get("X-Custom")).toBe("test-value"); + }); + }); +}); diff --git a/src/x402.test.ts b/src/x402.test.ts deleted file mode 100644 index 70c6d02..0000000 --- a/src/x402.test.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { describe, it, expect, afterEach, vi } from "vitest"; -import { verifyTypedData } from "viem"; -import { privateKeyToAccount } from "viem/accounts"; -import { createPaymentFetch } from "./x402.js"; - -const TEST_WALLET_KEY = `0x${"1".repeat(64)}` as `0x${string}`; - -const TRANSFER_TYPES = { - TransferWithAuthorization: [ - { name: "from", type: "address" }, - { name: "to", type: "address" }, - { name: "value", type: "uint256" }, - { name: "validAfter", type: "uint256" }, - { name: "validBefore", type: "uint256" }, - { name: "nonce", type: "bytes32" }, - ], -} as const; - -type PaymentPayload = { - resource: { - url: string; - description: string; - mimeType: string; - }; - accepted: { - scheme: string; - network: string; - amount: string; - asset: string; - payTo: string; - maxTimeoutSeconds?: number; - extra?: { name?: string; version?: string }; - }; - payload: { - signature: `0x${string}`; - authorization: { - from: `0x${string}`; - to: `0x${string}`; - value: string; - validAfter: string; - validBefore: string; - nonce: `0x${string}`; - }; - }; -}; - -function encodeBase64Json(value: unknown): string { - return Buffer.from(JSON.stringify(value), "utf8").toString("base64"); -} - -function encodeBase64Url(value: unknown): string { - return encodeBase64Json(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); -} - -function decodeBase64Json(value: string): T { - return JSON.parse(Buffer.from(value, "base64").toString("utf8")) as T; -} - -describe("x402 payment payload compatibility", () => { - afterEach(() => { - vi.unstubAllGlobals(); - vi.restoreAllMocks(); - }); - - it("uses 402 option fields for accepted/resource and sends both payment headers", async () => { - const option = { - scheme: "exact", - network: "eip155:84532", - amount: "4200", - asset: "0x1111111111111111111111111111111111111111", - payTo: "0x2222222222222222222222222222222222222222", - maxTimeoutSeconds: 120, - extra: { name: "USDC Mock", version: "1" }, - }; - const paymentRequired = { - accepts: [option], - resource: { - url: "https://blockrun.ai/api/v1/chat/completions", - description: "canonical endpoint", - }, - }; - - let retryHeaders: Headers | undefined; - const fetchMock = vi - .fn() - .mockResolvedValueOnce( - new Response("payment required", { - status: 402, - headers: { "x-payment-required": encodeBase64Url(paymentRequired) }, - }), - ) - .mockImplementationOnce(async (_input: RequestInfo | URL, init?: RequestInit) => { - retryHeaders = new Headers(init?.headers); - return new Response(JSON.stringify({ ok: true }), { status: 200 }); - }); - - vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch); - - const { fetch: payFetch } = createPaymentFetch(TEST_WALLET_KEY); - const response = await payFetch("https://blockrun.ai/api/v1/chat/completions?debug=1", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ model: "google/gemini-2.5-flash", messages: [] }), - }); - - expect(response.status).toBe(200); - expect(fetchMock).toHaveBeenCalledTimes(2); - expect(retryHeaders).toBeDefined(); - - const paymentSignature = retryHeaders!.get("payment-signature"); - expect(paymentSignature).toBeTruthy(); - expect(retryHeaders!.get("x-payment")).toBe(paymentSignature); - - const payload = decodeBase64Json(paymentSignature!); - expect(payload.accepted).toMatchObject({ - scheme: option.scheme, - network: option.network, - amount: option.amount, - asset: option.asset, - payTo: option.payTo, - maxTimeoutSeconds: option.maxTimeoutSeconds, - extra: option.extra, - }); - expect(payload.resource.url).toBe(paymentRequired.resource.url); - expect(payload.resource.description).toBe(paymentRequired.resource.description); - - const account = privateKeyToAccount(TEST_WALLET_KEY); - const isValid = await verifyTypedData({ - address: account.address, - domain: { - name: option.extra.name, - version: option.extra.version, - chainId: 84532, - verifyingContract: option.asset as `0x${string}`, - }, - types: TRANSFER_TYPES, - primaryType: "TransferWithAuthorization", - message: { - from: payload.payload.authorization.from, - to: payload.payload.authorization.to, - value: BigInt(payload.payload.authorization.value), - validAfter: BigInt(payload.payload.authorization.validAfter), - validBefore: BigInt(payload.payload.authorization.validBefore), - nonce: payload.payload.authorization.nonce, - }, - signature: payload.payload.signature, - }); - - expect(isValid).toBe(true); - }); - - it("reuses cached canonical resource URL for pre-auth payloads", async () => { - const option = { - scheme: "exact", - network: "eip155:8453", - maxAmountRequired: "1000", - asset: "0x3333333333333333333333333333333333333333", - payTo: "0x4444444444444444444444444444444444444444", - maxTimeoutSeconds: 90, - extra: { name: "USDC", version: "2" }, - }; - const paymentRequired = { - accepts: [option], - resource: { - url: "https://blockrun.ai/api/v1/chat/completions", - description: "canonical resource", - }, - }; - - let callCount = 0; - let preAuthHeaders: Headers | undefined; - const fetchMock = vi - .fn() - .mockImplementation(async (_input: RequestInfo | URL, init?: RequestInit) => { - callCount += 1; - - if (callCount === 1) { - return new Response("payment required", { - status: 402, - headers: { "x-payment-required": encodeBase64Json(paymentRequired) }, - }); - } - - if (callCount === 2) { - return new Response(JSON.stringify({ ok: true }), { status: 200 }); - } - - if (callCount === 3) { - preAuthHeaders = new Headers(init?.headers); - return new Response(JSON.stringify({ ok: true }), { status: 200 }); - } - - throw new Error(`Unexpected fetch call #${callCount}`); - }); - - vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch); - - const { fetch: payFetch } = createPaymentFetch(TEST_WALLET_KEY); - - // First request: 402 -> signed retry (fills payment cache). - await payFetch("https://blockrun.ai/api/v1/chat/completions?first=1", { - method: "POST", - body: JSON.stringify({ model: "moonshot/kimi-k2.5" }), - }); - - // Second request: pre-auth path should sign using cached canonical resource. - await payFetch( - "https://blockrun.ai/api/v1/chat/completions?second=1", - { - method: "POST", - body: JSON.stringify({ model: "moonshot/kimi-k2.5" }), - }, - { estimatedAmount: "2000" }, - ); - - expect(preAuthHeaders).toBeDefined(); - const paymentSignature = preAuthHeaders!.get("payment-signature"); - expect(paymentSignature).toBeTruthy(); - expect(preAuthHeaders!.get("x-payment")).toBe(paymentSignature); - - const payload = decodeBase64Json(paymentSignature!); - expect(payload.resource.url).toBe(paymentRequired.resource.url); - expect(payload.resource.description).toBe(paymentRequired.resource.description); - expect(payload.payload.authorization.value).toBe("2000"); - }); -}); diff --git a/src/x402.ts b/src/x402.ts deleted file mode 100644 index 2d0376c..0000000 --- a/src/x402.ts +++ /dev/null @@ -1,358 +0,0 @@ -/** - * x402 Payment Implementation - * - * Based on BlockRun's proven implementation. - * Handles 402 Payment Required responses with EIP-712 signed USDC transfers. - * - * Optimizations (v0.3.0): - * - Payment cache: after first 402, caches {payTo, asset, network} per endpoint. - * On subsequent requests, pre-signs payment and sends with first request, - * skipping the 402 round trip (~200ms savings). - * - Falls back to normal 402 flow if pre-signed payment is rejected. - */ - -import { signTypedData, privateKeyToAccount } from "viem/accounts"; -import { PaymentCache } from "./payment-cache.js"; - -const BASE_CHAIN_ID = 8453; -const BASE_SEPOLIA_CHAIN_ID = 84532; -const DEFAULT_TOKEN_NAME = "USD Coin"; -const DEFAULT_TOKEN_VERSION = "2"; -const DEFAULT_NETWORK = "eip155:8453"; -const DEFAULT_MAX_TIMEOUT_SECONDS = 300; - -const TRANSFER_TYPES = { - TransferWithAuthorization: [ - { name: "from", type: "address" }, - { name: "to", type: "address" }, - { name: "value", type: "uint256" }, - { name: "validAfter", type: "uint256" }, - { name: "validBefore", type: "uint256" }, - { name: "nonce", type: "bytes32" }, - ], -} as const; - -function createNonce(): `0x${string}` { - const bytes = new Uint8Array(32); - crypto.getRandomValues(bytes); - return `0x${Array.from(bytes) - .map((b) => b.toString(16).padStart(2, "0")) - .join("")}` as `0x${string}`; -} - -interface PaymentOption { - scheme: string; - network: string; - amount?: string; - maxAmountRequired?: string; - asset: string; - payTo: string; - maxTimeoutSeconds?: number; - extra?: { name?: string; version?: string }; -} - -interface PaymentRequired { - accepts: PaymentOption[]; - resource?: { url?: string; description?: string }; -} - -function decodeBase64Json(value: string): T { - const normalized = value.replace(/-/g, "+").replace(/_/g, "/"); - const padding = (4 - (normalized.length % 4)) % 4; - const padded = normalized + "=".repeat(padding); - const decoded = Buffer.from(padded, "base64").toString("utf8"); - return JSON.parse(decoded) as T; -} - -function encodeBase64Json(value: unknown): string { - return Buffer.from(JSON.stringify(value), "utf8").toString("base64"); -} - -function parsePaymentRequired(headerValue: string): PaymentRequired { - return decodeBase64Json(headerValue); -} - -function normalizeNetwork(network: string | undefined): string { - if (!network || network.trim().length === 0) { - return DEFAULT_NETWORK; - } - return network.trim().toLowerCase(); -} - -function resolveChainId(network: string): number { - const eip155Match = network.match(/^eip155:(\d+)$/i); - if (eip155Match) { - const parsed = Number.parseInt(eip155Match[1], 10); - if (Number.isFinite(parsed) && parsed > 0) { - return parsed; - } - } - - if (network === "base") return BASE_CHAIN_ID; - if (network === "base-sepolia") return BASE_SEPOLIA_CHAIN_ID; - return BASE_CHAIN_ID; -} - -function parseHexAddress(value: string | undefined): `0x${string}` | undefined { - if (!value) return undefined; - - const direct = value.match(/^0x[a-fA-F0-9]{40}$/); - if (direct) { - return direct[0] as `0x${string}`; - } - - // Some providers send CAIP-style assets (e.g. ".../erc20:0x..."). - const caipSuffix = value.match(/0x[a-fA-F0-9]{40}$/); - if (caipSuffix) { - return caipSuffix[0] as `0x${string}`; - } - - return undefined; -} - -function requireHexAddress(value: string | undefined, field: string): `0x${string}` { - const parsed = parseHexAddress(value); - if (!parsed) { - throw new Error(`Invalid ${field} in payment requirements: ${String(value)}`); - } - return parsed; -} - -function setPaymentHeaders(headers: Headers, payload: string): void { - // Support both modern and legacy header names for compatibility. - headers.set("payment-signature", payload); - headers.set("x-payment", payload); -} - -async function createPaymentPayload( - privateKey: `0x${string}`, - fromAddress: string, - option: PaymentOption, - amount: string, - requestUrl: string, - resource: PaymentRequired["resource"], -): Promise { - const network = normalizeNetwork(option.network); - const chainId = resolveChainId(network); - const recipient = requireHexAddress(option.payTo, "payTo"); - const verifyingContract = requireHexAddress(option.asset, "asset"); - - const maxTimeoutSeconds = - typeof option.maxTimeoutSeconds === "number" && option.maxTimeoutSeconds > 0 - ? Math.floor(option.maxTimeoutSeconds) - : DEFAULT_MAX_TIMEOUT_SECONDS; - - const now = Math.floor(Date.now() / 1000); - const validAfter = now - 600; - const validBefore = now + maxTimeoutSeconds; - const nonce = createNonce(); - - const signature = await signTypedData({ - privateKey, - domain: { - name: option.extra?.name || DEFAULT_TOKEN_NAME, - version: option.extra?.version || DEFAULT_TOKEN_VERSION, - chainId, - verifyingContract, - }, - types: TRANSFER_TYPES, - primaryType: "TransferWithAuthorization", - message: { - from: fromAddress as `0x${string}`, - to: recipient, - value: BigInt(amount), - validAfter: BigInt(validAfter), - validBefore: BigInt(validBefore), - nonce, - }, - }); - - const paymentData = { - x402Version: 2, - resource: { - url: resource?.url || requestUrl, - description: resource?.description || "BlockRun AI API call", - mimeType: "application/json", - }, - accepted: { - scheme: option.scheme, - network, - amount, - asset: option.asset, - payTo: option.payTo, - maxTimeoutSeconds: option.maxTimeoutSeconds, - extra: option.extra, - }, - payload: { - signature, - authorization: { - from: fromAddress, - to: recipient, - value: amount, - validAfter: validAfter.toString(), - validBefore: validBefore.toString(), - nonce, - }, - }, - extensions: {}, - }; - - return encodeBase64Json(paymentData); -} - -/** Pre-auth parameters for skipping the 402 round trip. */ -export type PreAuthParams = { - estimatedAmount: string; // USDC amount in smallest unit (6 decimals) -}; - -/** Result from createPaymentFetch — includes the fetch wrapper and payment cache. */ -export type PaymentFetchResult = { - fetch: ( - input: RequestInfo | URL, - init?: RequestInit, - preAuth?: PreAuthParams, - ) => Promise; - cache: PaymentCache; -}; - -/** - * Create a fetch wrapper that handles x402 payment automatically. - * - * Supports pre-auth: if cached payment params + estimated amount are available, - * pre-signs and attaches payment to the first request, skipping the 402 round trip. - * Falls back to normal 402 flow if pre-signed payment is rejected. - */ -export function createPaymentFetch(privateKey: `0x${string}`): PaymentFetchResult { - const account = privateKeyToAccount(privateKey); - const walletAddress = account.address; - const paymentCache = new PaymentCache(); - - const payFetch = async ( - input: RequestInfo | URL, - init?: RequestInit, - preAuth?: PreAuthParams, - ): Promise => { - const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; - const endpointPath = new URL(url).pathname; - - // --- Pre-auth path: skip 402 round trip --- - const cached = paymentCache.get(endpointPath); - if (cached && preAuth?.estimatedAmount) { - const paymentPayload = await createPaymentPayload( - privateKey, - walletAddress, - { - scheme: cached.scheme, - network: cached.network, - asset: cached.asset, - payTo: cached.payTo, - maxTimeoutSeconds: cached.maxTimeoutSeconds, - extra: cached.extra, - }, - preAuth.estimatedAmount, - url, - { - url: cached.resourceUrl, - description: cached.resourceDescription, - }, - ); - - const preAuthHeaders = new Headers(init?.headers); - setPaymentHeaders(preAuthHeaders, paymentPayload); - - const response = await fetch(input, { ...init, headers: preAuthHeaders }); - - // Pre-auth accepted — skip 402 entirely - if (response.status !== 402) { - return response; - } - - // Pre-auth rejected (wrong amount, payTo changed, etc.) - // Try to use this 402's payment header for a proper retry - const paymentHeader = response.headers.get("x-payment-required"); - if (paymentHeader) { - return handle402(input, init, url, endpointPath, paymentHeader); - } - - // No payment header — invalidate cache and retry clean (no payment header) - // to get a proper 402 with payment requirements - paymentCache.invalidate(endpointPath); - const cleanResponse = await fetch(input, init); - if (cleanResponse.status !== 402) { - return cleanResponse; - } - const cleanHeader = cleanResponse.headers.get("x-payment-required"); - if (!cleanHeader) { - throw new Error("402 response missing x-payment-required header"); - } - return handle402(input, init, url, endpointPath, cleanHeader); - } - - // --- Normal path: first request may get 402 --- - const response = await fetch(input, init); - - if (response.status !== 402) { - return response; - } - - const paymentHeader = response.headers.get("x-payment-required"); - if (!paymentHeader) { - throw new Error("402 response missing x-payment-required header"); - } - - return handle402(input, init, url, endpointPath, paymentHeader); - }; - - /** Handle a 402 response: parse, cache params, sign, retry. */ - async function handle402( - input: RequestInfo | URL, - init: RequestInit | undefined, - url: string, - endpointPath: string, - paymentHeader: string, - ): Promise { - const paymentRequired = parsePaymentRequired(paymentHeader); - const option = paymentRequired.accepts?.[0]; - if (!option) { - throw new Error("No payment options in 402 response"); - } - - const amount = option.amount || option.maxAmountRequired; - if (!amount) { - throw new Error("No amount in payment requirements"); - } - - // Cache payment params for future pre-auth - paymentCache.set(endpointPath, { - payTo: option.payTo, - asset: option.asset, - scheme: option.scheme, - network: option.network, - extra: option.extra, - maxTimeoutSeconds: option.maxTimeoutSeconds, - resourceUrl: paymentRequired.resource?.url, - resourceDescription: paymentRequired.resource?.description, - }); - - // Create signed payment - const paymentPayload = await createPaymentPayload( - privateKey, - walletAddress, - option, - amount, - url, - paymentRequired.resource, - ); - - // Retry with payment - const retryHeaders = new Headers(init?.headers); - setPaymentHeaders(retryHeaders, paymentPayload); - - return fetch(input, { - ...init, - headers: retryHeaders, - }); - } - - return { fetch: payFetch, cache: paymentCache }; -} diff --git a/test-auto-connection.mjs b/test-auto-connection.mjs index de8d47d..bf35628 100644 --- a/test-auto-connection.mjs +++ b/test-auto-connection.mjs @@ -18,7 +18,7 @@ console.log("=== ClawRouter Auto Model Diagnostic Test ===\n"); // Start proxy console.log("Starting proxy..."); const proxy = await startProxy({ - walletKey: WALLET_KEY, + wallet: WALLET_KEY, port: 8405, onReady: (port) => console.log(`āœ“ Proxy ready on port ${port}`), onError: (err) => console.error(`āœ— Proxy error: ${err.message}`), diff --git a/test/compression.ts b/test/compression.ts index bc4bd13..c6ee614 100644 --- a/test/compression.ts +++ b/test/compression.ts @@ -101,7 +101,7 @@ async function runTests() { // Start ClawRouter proxy pointing to mock API const proxy = await startProxy({ - walletKey: testWalletKey, + wallet: testWalletKey, apiBase: `http://127.0.0.1:${mockApi.port}`, port: 0, skipBalanceCheck: true, diff --git a/test/e2e-tool-id-sanitization.ts b/test/e2e-tool-id-sanitization.ts index 2135f19..f5c3362 100644 --- a/test/e2e-tool-id-sanitization.ts +++ b/test/e2e-tool-id-sanitization.ts @@ -46,7 +46,7 @@ async function main() { // Start proxy console.log("Starting proxy..."); proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port: TEST_PORT, onReady: (port) => console.log(`Proxy ready on port ${port}`), onError: (err) => console.error("Proxy error:", err.message), diff --git a/test/e2e.ts b/test/e2e.ts index b9b1733..60cc075 100644 --- a/test/e2e.ts +++ b/test/e2e.ts @@ -426,7 +426,7 @@ if (!walletKey) { } else { try { const proxy = await startProxy({ - walletKey, + wallet: walletKey, port: 0, onReady: (port) => console.log(` Proxy started on port ${port}`), onError: (err) => console.error(` Proxy error: ${err.message}`), diff --git a/test/fallback.ts b/test/fallback.ts index a2ab136..a017f90 100644 --- a/test/fallback.ts +++ b/test/fallback.ts @@ -110,7 +110,7 @@ async function runTests() { // Start ClawRouter proxy pointing to mock API const proxy = await startProxy({ - walletKey: testWalletKey, + wallet: testWalletKey, apiBase: `http://127.0.0.1:${mockApi.port}`, port: 0, skipBalanceCheck: true, // Skip balance check for testing diff --git a/test/google-messages.ts b/test/google-messages.ts index 33754fd..1bde2f5 100644 --- a/test/google-messages.ts +++ b/test/google-messages.ts @@ -98,7 +98,7 @@ async function runTests() { // Start ClawRouter proxy pointing to mock API const proxy = await startProxy({ - walletKey: testWalletKey, + wallet: testWalletKey, apiBase: `http://127.0.0.1:${mockApi.port}`, port: 0, skipBalanceCheck: true, diff --git a/test/integration/setup.ts b/test/integration/setup.ts index 031fe01..0a2d87f 100644 --- a/test/integration/setup.ts +++ b/test/integration/setup.ts @@ -27,7 +27,7 @@ export async function startTestProxy(): Promise { const wallet = await resolveOrGenerateWalletKey(); proxyHandle = await startProxy({ - walletKey: wallet.key, + wallet, port: TEST_PORT, skipBalanceCheck: true, }); diff --git a/test/proxy-reuse.ts b/test/proxy-reuse.ts index 6a3048a..0366521 100644 --- a/test/proxy-reuse.ts +++ b/test/proxy-reuse.ts @@ -89,7 +89,7 @@ console.log("\n═══ Part 2: Proxy Reuse ═══\n"); // Start first proxy console.log("\n Starting first proxy..."); const proxy1 = await startProxy({ - walletKey, + wallet: walletKey, port: testPort, onReady: (port) => console.log(` First proxy ready on port ${port}`), }); @@ -108,7 +108,7 @@ console.log("\n═══ Part 2: Proxy Reuse ═══\n"); // Start second proxy on same port — should reuse console.log("\n Starting second proxy (should reuse)..."); const proxy2 = await startProxy({ - walletKey, + wallet: walletKey, port: testPort, onReady: (port) => console.log(` Second proxy ready on port ${port}`), }); @@ -174,7 +174,7 @@ console.log("\n═══ Part 3: Different Wallet Warning ═══\n"); // Start proxy with wallet 1 console.log("\n Starting proxy with wallet 1..."); const proxy1 = await startProxy({ - walletKey: walletKey1, + wallet: walletKey1, port: testPort, onReady: (port) => console.log(` Proxy 1 ready on port ${port}`), }); @@ -184,7 +184,7 @@ console.log("\n═══ Part 3: Different Wallet Warning ═══\n"); // Start proxy with wallet 2 on same port — should reuse but return existing wallet console.log("\n Starting proxy with wallet 2 (should reuse existing with wallet 1)..."); const proxy2 = await startProxy({ - walletKey: walletKey2, + wallet: walletKey2, port: testPort, onReady: (port) => console.log(` Proxy 2 ready on port ${port}`), }); diff --git a/test/resilience-errors.ts b/test/resilience-errors.ts index 4ddb2cc..446b109 100644 --- a/test/resilience-errors.ts +++ b/test/resilience-errors.ts @@ -86,7 +86,7 @@ async function setupTestEnvironment(): Promise { // Start proxy pointing to mock server const proxy = await startProxy({ - walletKey: TEST_WALLET, + wallet: TEST_WALLET, apiBase: `http://127.0.0.1:${mockPort}`, skipBalanceCheck: true, }); @@ -161,7 +161,7 @@ async function testEconnreset(ctx: TestContext): Promise { // Create proxy pointing to reset server const resetProxy = await startProxy({ - walletKey: TEST_WALLET, + wallet: TEST_WALLET, apiBase: `http://127.0.0.1:${resetPort}`, skipBalanceCheck: true, }); @@ -214,7 +214,7 @@ async function testClientTimeout(ctx: TestContext): Promise { const slowPort = (slowServer.address() as { port: number }).port; const slowProxy = await startProxy({ - walletKey: TEST_WALLET, + wallet: TEST_WALLET, apiBase: `http://127.0.0.1:${slowPort}`, skipBalanceCheck: true, requestTimeoutMs: 1000, // 1s timeout diff --git a/test/resilience-lifecycle.ts b/test/resilience-lifecycle.ts index 58a6d92..bc3d187 100644 --- a/test/resilience-lifecycle.ts +++ b/test/resilience-lifecycle.ts @@ -81,7 +81,7 @@ async function setupTestEnvironment(): Promise { const mockPort = (mockServer.address() as { port: number }).port; const proxy = await startProxy({ - walletKey: TEST_WALLET, + wallet: TEST_WALLET, apiBase: `http://127.0.0.1:${mockPort}`, skipBalanceCheck: true, }); diff --git a/test/resilience-stability.ts b/test/resilience-stability.ts index d532d96..70a5bf4 100644 --- a/test/resilience-stability.ts +++ b/test/resilience-stability.ts @@ -92,7 +92,7 @@ async function setupTestEnvironment(): Promise { const mockPort = (mockServer.address() as { port: number }).port; const proxy = await startProxy({ - walletKey: TEST_WALLET, + wallet: TEST_WALLET, apiBase: `http://127.0.0.1:${mockPort}`, skipBalanceCheck: true, }); diff --git a/test/smoke-wallet-scenarios.ts b/test/smoke-wallet-scenarios.ts new file mode 100644 index 0000000..c9b571e --- /dev/null +++ b/test/smoke-wallet-scenarios.ts @@ -0,0 +1,182 @@ +/** + * Manual wallet migration smoke test on real disk. + * + * Tests all 3 scenarios using a temp HOME to avoid touching real wallet. + * Uses real filesystem, real path resolution, real file permissions. + * + * Usage: npx tsx test/smoke-wallet-scenarios.ts + */ + +import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; + +// Create temp HOME so we don't touch real wallet +const TEMP_HOME = join(tmpdir(), `clawrouter-smoke-${Date.now()}`); +mkdirSync(TEMP_HOME, { recursive: true }); +process.env.HOME = TEMP_HOME; + +// Now import auth (it reads homedir() at module load) +const { resolveOrGenerateWalletKey, setupSolana, WALLET_FILE, MNEMONIC_FILE } = + await import("../src/auth.js"); +const { isValidMnemonic } = await import("../src/wallet.js"); + +const WALLET_DIR = join(TEMP_HOME, ".openclaw", "blockrun"); + +let passed = 0; +let failed = 0; + +function assert(ok: boolean, msg: string) { + if (ok) { console.log(` āœ“ ${msg}`); passed++; } + else { console.error(` āœ— FAIL: ${msg}`); failed++; } +} + +function clean() { + if (existsSync(WALLET_DIR)) { + rmSync(WALLET_DIR, { recursive: true }); + } +} + +// ═══════════════════════════════════════════════════════════════ +// Scenario 3: Fresh install (nothing exists) +// Expected: generate mnemonic + both keys +// ═══════════════════════════════════════════════════════════════ + +console.log("\n═══ Scenario 3: Fresh Install ═══\n"); +clean(); + +{ + const result = await resolveOrGenerateWalletKey(); + + assert(result.source === "generated", `Source: ${result.source}`); + assert(result.key.startsWith("0x") && result.key.length === 66, `EVM key valid: ${result.key.slice(0, 12)}...`); + assert(result.address.startsWith("0x"), `EVM address: ${result.address}`); + assert(result.mnemonic !== undefined, `Mnemonic returned`); + assert(result.solanaPrivateKeyBytes !== undefined, `Solana key bytes returned`); + assert(result.solanaPrivateKeyBytes!.length === 32, `Solana key is 32 bytes`); + + // Verify files on disk + assert(existsSync(WALLET_FILE), `wallet.key exists on disk`); + assert(existsSync(MNEMONIC_FILE), `mnemonic exists on disk`); + + const diskKey = readFileSync(WALLET_FILE, "utf8").trim(); + assert(diskKey === result.key, `wallet.key content matches`); + + const diskMnemonic = readFileSync(MNEMONIC_FILE, "utf8").trim(); + assert(isValidMnemonic(diskMnemonic), `mnemonic on disk is valid BIP-39`); + assert(diskMnemonic === result.mnemonic, `mnemonic content matches`); + + // Verify file permissions (0o600 = owner read/write only) + const { statSync } = await import("node:fs"); + const keyPerms = statSync(WALLET_FILE).mode & 0o777; + assert(keyPerms === 0o600, `wallet.key permissions: ${keyPerms.toString(8)} (expect 600)`); + const mnemonicPerms = statSync(MNEMONIC_FILE).mode & 0o777; + assert(mnemonicPerms === 0o600, `mnemonic permissions: ${mnemonicPerms.toString(8)} (expect 600)`); +} + +// ═══════════════════════════════════════════════════════════════ +// Scenario 1: Existing wallet.key only (no mnemonic) +// Expected: EVM-only, no Solana keys +// ═══════════════════════════════════════════════════════════════ + +console.log("\n═══ Scenario 1: Existing wallet.key Only ═══\n"); +clean(); + +{ + // Set up: create wallet.key but no mnemonic + mkdirSync(WALLET_DIR, { recursive: true }); + const testKey = "0x" + "ab".repeat(32); + writeFileSync(join(WALLET_DIR, "wallet.key"), testKey + "\n", { mode: 0o600 }); + + const result = await resolveOrGenerateWalletKey(); + + assert(result.source === "saved", `Source: ${result.source}`); + assert(result.key === testKey, `EVM key loaded from disk`); + assert(result.address.startsWith("0x"), `EVM address: ${result.address}`); + assert(result.mnemonic === undefined, `No mnemonic (EVM-only)`); + assert(result.solanaPrivateKeyBytes === undefined, `No Solana key bytes (EVM-only)`); +} + +// ═══════════════════════════════════════════════════════════════ +// Scenario 2: Existing wallet.key + setup-solana +// Expected: creates mnemonic, derives Solana, EVM untouched +// ═══════════════════════════════════════════════════════════════ + +console.log("\n═══ Scenario 2: wallet.key + setup-solana ═══\n"); +// Don't clean - reuse wallet.key from scenario 1 + +{ + const originalKey = readFileSync(join(WALLET_DIR, "wallet.key"), "utf8").trim(); + assert(!existsSync(MNEMONIC_FILE), `No mnemonic before setup-solana`); + + // Run setup-solana + const solResult = await setupSolana(); + + assert(typeof solResult.mnemonic === "string", `Mnemonic returned from setup-solana`); + assert(isValidMnemonic(solResult.mnemonic), `Mnemonic is valid BIP-39`); + assert(solResult.solanaPrivateKeyBytes.length === 32, `Solana key is 32 bytes`); + assert(existsSync(MNEMONIC_FILE), `Mnemonic file created on disk`); + + // EVM wallet.key must be UNTOUCHED + const afterKey = readFileSync(join(WALLET_DIR, "wallet.key"), "utf8").trim(); + assert(afterKey === originalKey, `EVM wallet.key unchanged after setup-solana`); + + // Now resolveOrGenerateWalletKey should return both + const result = await resolveOrGenerateWalletKey(); + assert(result.source === "saved", `Source: ${result.source}`); + assert(result.key === originalKey, `EVM key still matches`); + assert(result.solanaPrivateKeyBytes !== undefined, `Solana key bytes now available`); + assert(result.mnemonic !== undefined, `Mnemonic now available`); +} + +// ═══════════════════════════════════════════════════════════════ +// Edge case: Delete wallet.key when mnemonic exists +// Expected: refuse to generate new wallet (protect Solana funds) +// ═══════════════════════════════════════════════════════════════ + +console.log("\n═══ Edge Case: Delete wallet.key with mnemonic present ═══\n"); + +{ + // Remove wallet.key but keep mnemonic + rmSync(join(WALLET_DIR, "wallet.key")); + assert(!existsSync(join(WALLET_DIR, "wallet.key")), `wallet.key deleted`); + assert(existsSync(MNEMONIC_FILE), `mnemonic still exists`); + + try { + await resolveOrGenerateWalletKey(); + assert(false, `Should have thrown but didn't`); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + assert(msg.includes("Refusing"), `Threw protective error: ${msg.slice(0, 80)}...`); + } +} + +// ═══════════════════════════════════════════════════════════════ +// Edge case: setup-solana when mnemonic already exists +// Expected: refuse (don't overwrite) +// ═══════════════════════════════════════════════════════════════ + +console.log("\n═══ Edge Case: setup-solana when already set up ═══\n"); + +{ + // Restore wallet.key for this test + writeFileSync(join(WALLET_DIR, "wallet.key"), "0x" + "ab".repeat(32) + "\n", { mode: 0o600 }); + + try { + await setupSolana(); + assert(false, `Should have thrown but didn't`); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + assert(msg.includes("already set up"), `Threw: ${msg.slice(0, 60)}...`); + } +} + +// Cleanup +clean(); +rmSync(TEMP_HOME, { recursive: true }); + +console.log("\n═══════════════════════════════════"); +console.log(` ${passed} passed, ${failed} failed`); +console.log("═══════════════════════════════════\n"); + +process.exit(failed > 0 ? 1 : 0); diff --git a/test/test-balance-integration.ts b/test/test-balance-integration.ts index 39c06f8..92778f2 100644 --- a/test/test-balance-integration.ts +++ b/test/test-balance-integration.ts @@ -56,7 +56,7 @@ async function main() { // Start proxy with empty wallet console.log("Starting proxy with empty wallet..."); const proxy = await startProxy({ - walletKey: emptyWalletKey, + wallet: emptyWalletKey, onReady: (port) => console.log(`Proxy ready on port ${port}`), onError: (err) => console.log(`[onError] ${err.message}`), onLowBalance: (info) => { diff --git a/test/test-clawrouter.mjs b/test/test-clawrouter.mjs index 24c5e2c..cff7358 100644 --- a/test/test-clawrouter.mjs +++ b/test/test-clawrouter.mjs @@ -571,7 +571,7 @@ await testAsync("Proxy starts on specified port", async () => { const port = 18402 + Math.floor(Math.random() * 1000); let readyPort = null; const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: (p) => { readyPort = p; @@ -585,7 +585,7 @@ await testAsync("Proxy starts on specified port", async () => { await testAsync("Proxy health endpoint works", async () => { const port = 18402 + Math.floor(Math.random() * 1000); const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: () => {}, onError: () => {}, @@ -603,7 +603,7 @@ await testAsync("Proxy health endpoint works", async () => { await testAsync("Proxy close frees port", async () => { const port = 18402 + Math.floor(Math.random() * 1000); const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: () => {}, onError: () => {}, @@ -612,7 +612,7 @@ await testAsync("Proxy close frees port", async () => { // Should be able to start another proxy on same port const proxy2 = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: () => {}, onError: () => {}, @@ -623,7 +623,7 @@ await testAsync("Proxy close frees port", async () => { await testAsync("Proxy returns 404 for unknown routes", async () => { const port = 18402 + Math.floor(Math.random() * 1000); const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: () => {}, onError: () => {}, @@ -638,7 +638,7 @@ await testAsync("Proxy returns 404 for unknown routes", async () => { await testAsync("Proxy health returns wallet address", async () => { const port = 18402 + Math.floor(Math.random() * 1000); const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: () => {}, onError: () => {}, @@ -655,7 +655,7 @@ await testAsync("Proxy health returns wallet address", async () => { await testAsync("Proxy handles concurrent health checks", async () => { const port = 18402 + Math.floor(Math.random() * 1000); const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, onReady: () => {}, onError: () => {}, @@ -677,7 +677,7 @@ await testAsync("Proxy handles concurrent health checks", async () => { await testAsync("Proxy models endpoint returns model list", async () => { const port = 18402 + Math.floor(Math.random() * 1000); const proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, port, skipBalanceCheck: true, // Skip balance check for testing onReady: () => {}, diff --git a/test/test-e2e.mjs b/test/test-e2e.mjs index c7a8689..cb16ba2 100644 --- a/test/test-e2e.mjs +++ b/test/test-e2e.mjs @@ -128,7 +128,7 @@ async function runTests() { let proxy; try { proxy = await startProxy({ - walletKey: TEST_WALLET_KEY, + wallet: TEST_WALLET_KEY, apiBase: `http://127.0.0.1:${mockPort}`, port: 0, onReady: (port) => console.log(`āœ“ ClawRouter proxy started on port ${port}`), diff --git a/test/test-e2e.ts b/test/test-e2e.ts index 1bf3278..4a638cb 100644 --- a/test/test-e2e.ts +++ b/test/test-e2e.ts @@ -101,7 +101,7 @@ async function main() { // Start proxy console.log("Starting proxy..."); const proxy = await startProxy({ - walletKey: WALLET_KEY, + wallet: WALLET_KEY, port: 0, onReady: (port) => console.log(`Proxy ready on port ${port}`), onError: (err) => console.error(`Proxy error: ${err.message}`), diff --git a/test/test-solana-e2e.ts b/test/test-solana-e2e.ts new file mode 100644 index 0000000..16ed164 --- /dev/null +++ b/test/test-solana-e2e.ts @@ -0,0 +1,150 @@ +/** + * End-to-end test for Solana x402 payment integration. + * + * Validates that ClawRouter correctly: + * 1. Derives both EVM and Solana wallets from a BIP-39 mnemonic + * 2. Registers both EVM and Solana x402 schemes + * 3. Connects to the real BlockRun API + * 4. Handles the 402 payment flow (payment attempt, even if wallet is unfunded) + * + * Usage: + * npx tsx test/test-solana-e2e.ts + * + * With a funded wallet (full E2E): + * BLOCKRUN_WALLET_KEY=0x... npx tsx test/test-solana-e2e.ts + */ + +import { startProxy } from "../src/proxy.js"; +import { generateWalletMnemonic, deriveEvmKey, deriveSolanaKeyBytes } from "../src/wallet.js"; + +let passed = 0; +let failed = 0; + +function assert(condition: boolean, msg: string) { + if (condition) { + console.log(` āœ“ ${msg}`); + passed++; + } else { + console.error(` āœ— FAIL: ${msg}`); + failed++; + } +} + +async function run() { + console.log("\n═══ Solana E2E Integration Test ═══\n"); + + // --- Part 1: Wallet Derivation --- + console.log("--- Part 1: Wallet derivation from mnemonic ---\n"); + + const mnemonic = generateWalletMnemonic(); + assert(mnemonic.split(" ").length === 24, "Generated 24-word mnemonic"); + + const evm = deriveEvmKey(mnemonic); + assert(evm.privateKey.startsWith("0x") && evm.privateKey.length === 66, `EVM key derived: ${evm.privateKey.slice(0, 10)}...`); + + const solanaBytes = deriveSolanaKeyBytes(mnemonic); + assert(solanaBytes.length === 32, `Solana private key derived: ${solanaBytes.length} bytes`); + + // Verify Solana signer creation + const { createKeyPairSignerFromPrivateKeyBytes } = await import("@solana/kit"); + const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes(solanaBytes); + assert(typeof solanaSigner.address === "string" && solanaSigner.address.length > 30, `Solana address: ${solanaSigner.address}`); + + // --- Part 2: Proxy with dual-chain support --- + console.log("\n--- Part 2: Proxy startup with both chains ---\n"); + + // Use env wallet if available, otherwise use the generated one + const walletKey = process.env.BLOCKRUN_WALLET_KEY ?? evm.privateKey; + const solanaKeyBytes = process.env.BLOCKRUN_WALLET_KEY ? undefined : solanaBytes; + + const proxy = await startProxy({ + wallet: { key: walletKey, solanaPrivateKeyBytes: solanaKeyBytes }, + port: 0, + skipBalanceCheck: true, + onReady: (port) => console.log(` Proxy started on port ${port}`), + }); + + assert(proxy.port > 0, `Proxy listening on port ${proxy.port}`); + assert(typeof proxy.walletAddress === "string", `EVM wallet: ${proxy.walletAddress}`); + + // --- Part 3: Health check --- + console.log("\n--- Part 3: Health endpoint ---\n"); + + const healthRes = await fetch(`http://127.0.0.1:${proxy.port}/health`); + const healthData = (await healthRes.json()) as { status: string; wallet: string; solana?: string }; + assert(healthData.status === "ok", `Health status: ${healthData.status}`); + assert(typeof healthData.wallet === "string", `Health reports EVM wallet: ${healthData.wallet}`); + + // --- Part 4: Real API request (402 payment flow) --- + console.log("\n--- Part 4: BlockRun API request (402 flow) ---\n"); + + try { + const res = await fetch(`http://127.0.0.1:${proxy.port}/v1/chat/completions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: "deepseek/deepseek-chat", + messages: [{ role: "user", content: "Say hello" }], + max_tokens: 10, + }), + signal: AbortSignal.timeout(30_000), + }); + + if (res.ok) { + // Wallet was funded - full E2E success + const text = await res.text(); + assert(true, `Request succeeded (funded wallet): status ${res.status}`); + assert(text.length > 0, `Response body received: ${text.length} chars`); + } else { + // 402 flow was attempted but payment failed (expected for unfunded wallet) + const body = await res.text(); + const isPaymentError = + body.includes("insufficient") || + body.includes("balance") || + body.includes("payment") || + body.includes("402") || + body.includes("fund") || + res.status === 402; + + assert( + isPaymentError || res.status === 500, + `Payment flow reached (status ${res.status}): ${body.slice(0, 200)}`, + ); + } + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + // Connection errors mean the proxy or API is unreachable - that's a real failure + // Payment/timeout errors mean the 402 flow was attempted - that's success + const isExpected = msg.includes("timeout") || msg.includes("abort"); + assert(isExpected, `Request outcome: ${msg.slice(0, 200)}`); + } + + // --- Part 5: Models endpoint --- + console.log("\n--- Part 5: Models endpoint reachable ---\n"); + + try { + const modelsRes = await fetch(`http://127.0.0.1:${proxy.port}/v1/models`, { + signal: AbortSignal.timeout(10_000), + }); + assert(modelsRes.ok, `Models endpoint: status ${modelsRes.status}`); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + assert(false, `Models endpoint failed: ${msg}`); + } + + // Cleanup + await proxy.close(); + console.log("\n Proxy closed."); + + // Summary + console.log("\n═══════════════════════════════════"); + console.log(` ${passed} passed, ${failed} failed`); + console.log("═══════════════════════════════════\n"); + + process.exit(failed > 0 ? 1 : 0); +} + +run().catch((err) => { + console.error("Fatal:", err); + process.exit(1); +});